fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / s390 / cio / chsc.c
index 72187e5..cbab8d2 100644 (file)
@@ -10,7 +10,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/device.h>
@@ -184,7 +183,7 @@ css_get_ssd_info(struct subchannel *sch)
        page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
        if (!page)
                return -ENOMEM;
-       spin_lock_irq(&sch->lock);
+       spin_lock_irq(sch->lock);
        ret = chsc_get_sch_desc_irq(sch, page);
        if (ret) {
                static int cio_chsc_err_msg;
@@ -198,14 +197,16 @@ css_get_ssd_info(struct subchannel *sch)
                        cio_chsc_err_msg = 1;
                }
        }
-       spin_unlock_irq(&sch->lock);
+       spin_unlock_irq(sch->lock);
        free_page((unsigned long)page);
        if (!ret) {
-               int j, chpid;
+               int j, chpid, mask;
                /* Allocate channel path structures, if needed. */
                for (j = 0; j < 8; j++) {
+                       mask = 0x80 >> j;
                        chpid = sch->ssd_info.chpid[j];
-                       if (chpid && (get_chp_status(chpid) < 0))
+                       if ((sch->schib.pmcw.pim & mask) &&
+                           (get_chp_status(chpid) < 0))
                            new_channel_path(chpid);
                }
        }
@@ -223,14 +224,16 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
 
        sch = to_subchannel(dev);
        chpid = data;
-       for (j = 0; j < 8; j++)
-               if (sch->schib.pmcw.chpid[j] == chpid->id)
+       for (j = 0; j < 8; j++) {
+               mask = 0x80 >> j;
+               if ((sch->schib.pmcw.pim & mask) &&
+                   (sch->schib.pmcw.chpid[j] == chpid->id))
                        break;
+       }
        if (j >= 8)
                return 0;
 
-       mask = 0x80 >> j;
-       spin_lock_irq(&sch->lock);
+       spin_lock_irq(sch->lock);
 
        stsch(sch->schid, &schib);
        if (!schib.pmcw.dnv)
@@ -239,18 +242,17 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
        /* Check for single path devices. */
        if (sch->schib.pmcw.pim == 0x80)
                goto out_unreg;
-       if (sch->vpm == mask)
-               goto out_unreg;
 
        if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) &&
            (sch->schib.scsw.actl & SCSW_ACTL_SCHACT) &&
-           (sch->schib.pmcw.lpum == mask) &&
-           (sch->vpm == 0)) {
+           (sch->schib.pmcw.lpum == mask)) {
                int cc;
 
                cc = cio_clear(sch);
                if (cc == -ENODEV)
                        goto out_unreg;
+               /* Request retry of internal operation. */
+               device_set_intretry(sch);
                /* Call handler. */
                if (sch->driver && sch->driver->termination)
                        sch->driver->termination(&sch->dev);
@@ -260,11 +262,13 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
        /* trigger path verification. */
        if (sch->driver && sch->driver->verify)
                sch->driver->verify(&sch->dev);
+       else if (sch->lpm == mask)
+               goto out_unreg;
 out_unlock:
-       spin_unlock_irq(&sch->lock);
+       spin_unlock_irq(sch->lock);
        return 0;
 out_unreg:
-       spin_unlock_irq(&sch->lock);
+       spin_unlock_irq(sch->lock);
        sch->lpm = 0;
        if (css_enqueue_subchannel_slow(sch->schid)) {
                css_clear_subchannel_slow_list();
@@ -368,18 +372,19 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
        struct res_acc_data *res_data;
        struct subchannel *sch;
 
-       res_data = (struct res_acc_data *)data;
+       res_data = data;
        sch = get_subchannel_by_schid(schid);
        if (!sch)
                /* Check if a subchannel is newly available. */
                return s390_process_res_acc_new_sch(schid);
 
-       spin_lock_irq(&sch->lock);
+       spin_lock_irq(sch->lock);
 
        chp_mask = s390_process_res_acc_sch(res_data, sch);
 
        if (chp_mask == 0) {
-               spin_unlock_irq(&sch->lock);
+               spin_unlock_irq(sch->lock);
+               put_device(&sch->dev);
                return 0;
        }
        old_lpm = sch->lpm;
@@ -392,9 +397,9 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
        else if (sch->driver && sch->driver->verify)
                sch->driver->verify(&sch->dev);
 
-       spin_unlock_irq(&sch->lock);
+       spin_unlock_irq(sch->lock);
        put_device(&sch->dev);
-       return (res_data->fla_mask == 0xffff) ? -ENODEV : 0;
+       return 0;
 }
 
 
@@ -441,7 +446,7 @@ __get_chpid_from_lir(void *data)
                u32 isinfo[28];
        } *lir;
 
-       lir = (struct lir*) data;
+       lir = data;
        if (!(lir->iq&0x80))
                /* NULL link incident record */
                return -EINVAL;
@@ -621,38 +626,41 @@ __chp_add_new_sch(struct subchannel_id schid)
 static int
 __chp_add(struct subchannel_id schid, void *data)
 {
-       int i;
+       int i, mask;
        struct channel_path *chp;
        struct subchannel *sch;
 
-       chp = (struct channel_path *)data;
+       chp = data;
        sch = get_subchannel_by_schid(schid);
        if (!sch)
                /* Check if the subchannel is now available. */
                return __chp_add_new_sch(schid);
-       spin_lock_irq(&sch->lock);
-       for (i=0; i<8; i++)
-               if (sch->schib.pmcw.chpid[i] == chp->id) {
+       spin_lock_irq(sch->lock);
+       for (i=0; i<8; i++) {
+               mask = 0x80 >> i;
+               if ((sch->schib.pmcw.pim & mask) &&
+                   (sch->schib.pmcw.chpid[i] == chp->id)) {
                        if (stsch(sch->schid, &sch->schib) != 0) {
                                /* Endgame. */
-                               spin_unlock_irq(&sch->lock);
+                               spin_unlock_irq(sch->lock);
                                return -ENXIO;
                        }
                        break;
                }
+       }
        if (i==8) {
-               spin_unlock_irq(&sch->lock);
+               spin_unlock_irq(sch->lock);
                return 0;
        }
        sch->lpm = ((sch->schib.pmcw.pim &
                     sch->schib.pmcw.pam &
                     sch->schib.pmcw.pom)
-                   | 0x80 >> i) & sch->opm;
+                   | mask) & sch->opm;
 
        if (sch->driver && sch->driver->verify)
                sch->driver->verify(&sch->dev);
 
-       spin_unlock_irq(&sch->lock);
+       spin_unlock_irq(sch->lock);
        put_device(&sch->dev);
        return 0;
 }
@@ -701,24 +709,38 @@ chp_process_crw(int chpid, int on)
        return chp_add(chpid);
 }
 
-static inline int
-__check_for_io_and_kill(struct subchannel *sch, int index)
+static inline int check_for_io_on_path(struct subchannel *sch, int index)
 {
        int cc;
 
-       if (!device_is_online(sch))
-               /* cio could be doing I/O. */
-               return 0;
        cc = stsch(sch->schid, &sch->schib);
        if (cc)
                return 0;
-       if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == (0x80 >> index)) {
-               device_set_waiting(sch);
+       if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == (0x80 >> index))
                return 1;
-       }
        return 0;
 }
 
+static void terminate_internal_io(struct subchannel *sch)
+{
+       if (cio_clear(sch)) {
+               /* Recheck device in case clear failed. */
+               sch->lpm = 0;
+               if (device_trigger_verify(sch) != 0) {
+                       if(css_enqueue_subchannel_slow(sch->schid)) {
+                               css_clear_subchannel_slow_list();
+                               need_rescan = 1;
+                       }
+               }
+               return;
+       }
+       /* Request retry of internal operation. */
+       device_set_intretry(sch);
+       /* Call handler. */
+       if (sch->driver && sch->driver->termination)
+               sch->driver->termination(&sch->dev);
+}
+
 static inline void
 __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
 {
@@ -728,7 +750,7 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
        if (!sch->ssd_info.valid)
                return;
        
-       spin_lock_irqsave(&sch->lock, flags);
+       spin_lock_irqsave(sch->lock, flags);
        old_lpm = sch->lpm;
        for (chp = 0; chp < 8; chp++) {
                if (sch->ssd_info.chpid[chp] != chpid)
@@ -741,25 +763,29 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
                                device_trigger_reprobe(sch);
                        else if (sch->driver && sch->driver->verify)
                                sch->driver->verify(&sch->dev);
-               } else {
-                       sch->opm &= ~(0x80 >> chp);
-                       sch->lpm &= ~(0x80 >> chp);
-                       /*
-                        * Give running I/O a grace period in which it
-                        * can successfully terminate, even using the
-                        * just varied off path. Then kill it.
-                        */
-                       if (!__check_for_io_and_kill(sch, chp) && !sch->lpm) {
+                       break;
+               }
+               sch->opm &= ~(0x80 >> chp);
+               sch->lpm &= ~(0x80 >> chp);
+               if (check_for_io_on_path(sch, chp)) {
+                       if (device_is_online(sch))
+                               /* Path verification is done after killing. */
+                               device_kill_io(sch);
+                       else
+                               /* Kill and retry internal I/O. */
+                               terminate_internal_io(sch);
+               } else if (!sch->lpm) {
+                       if (device_trigger_verify(sch) != 0) {
                                if (css_enqueue_subchannel_slow(sch->schid)) {
                                        css_clear_subchannel_slow_list();
                                        need_rescan = 1;
                                }
-                       } else if (sch->driver && sch->driver->verify)
-                               sch->driver->verify(&sch->dev);
-               }
+                       }
+               } else if (sch->driver && sch->driver->verify)
+                       sch->driver->verify(&sch->dev);
                break;
        }
-       spin_unlock_irqrestore(&sch->lock, flags);
+       spin_unlock_irqrestore(sch->lock, flags);
 }
 
 static int
@@ -918,12 +944,13 @@ chp_measurement_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
        chp = to_channelpath(container_of(kobj, struct device, kobj));
        css = to_css(chp->dev.parent);
 
-       size = sizeof(struct cmg_chars);
+       size = sizeof(struct cmg_entry);
 
        /* Only allow single reads. */
        if (off || count < size)
                return 0;
        chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->id);
+       count = size;
        return count;
 }
 
@@ -1392,10 +1419,8 @@ new_channel_path(int chpid)
        /* fill in status, etc. */
        chp->id = chpid;
        chp->state = 1;
-       chp->dev = (struct device) {
-               .parent  = &css[0]->device,
-               .release = chp_release,
-       };
+       chp->dev.parent = &css[0]->device;
+       chp->dev.release = chp_release;
        snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp0.%x", chpid);
 
        /* Obtain channel path description and fill it in. */
@@ -1465,7 +1490,6 @@ chsc_get_chp_desc(struct subchannel *sch, int chp_no)
        return desc;
 }
 
-
 static int __init
 chsc_alloc_sei_area(void)
 {