]> git.hungrycats.org Git - linux/commitdiff
[PATCH] s390: common i/o layer.
authorAndrew Morton <akpm@osdl.org>
Tue, 16 Mar 2004 23:07:59 +0000 (15:07 -0800)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Tue, 16 Mar 2004 23:07:59 +0000 (15:07 -0800)
From: Martin Schwidefsky <schwidefsky@de.ibm.com>

Common i/o layer fixes:
 - Improve blacklist argument parsing.
 - Fix device recognition for devices where SenseID fails.
 - Don't try to set a device online that has no driver.
 - Chain a release ccw to the unconditional reserve ccw for forced online.
 - Fix irb accumulation for pure status pending with eswf set.
 - Fix rc handling in qdio_shutdown.
 - Improve retry behavious for busy conditions on qdio.
 - Fix activity check in ccw_device_start/read_dev_chars and read_conf_data.

Documentation/s390/driver-model.txt
drivers/s390/cio/blacklist.c
drivers/s390/cio/css.h
drivers/s390/cio/device.c
drivers/s390/cio/device_fsm.c
drivers/s390/cio/device_ops.c
drivers/s390/cio/device_status.c
drivers/s390/cio/qdio.c
drivers/s390/cio/qdio.h
include/asm-s390/cio.h

index 51322ac8b1ce95329a04d032b97dbeeb2ffaff4c..19461958e2bd98a24260866329eb2e26b5188dbd 100644 (file)
@@ -250,7 +250,7 @@ xpram shows up under sys/ as 'xpram'.
 -----------
 
 The netiucv driver creates an attribute 'connection' under
-bus/iucv/drivers/NETIUCV. Piping to this attibute creates a new netiucv
+bus/iucv/drivers/netiucv. Piping to this attibute creates a new netiucv
 connection to the specified host.
 
 Netiucv connections show up under devices/iucv/ as "netiucv<ifnum>". The interface
index 6cfda026d07454c24107ac6b306b40aa2e746d03..0589933b9add577189a68ec55224a95ef3a41caf 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/blacklist.c
  *   S/390 common I/O routines -- blacklisting of specific devices
- *   $Revision: 1.29 $
+ *   $Revision: 1.31 $
  *
  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  *                           IBM Corporation
@@ -72,7 +72,10 @@ static inline int
 blacklist_busid(char **str, int *id0, int *id1, int *devno)
 {
        int val, old_style;
- 
+       char *sav;
+
+       sav = *str;
+
        /* check for leading '0x' */
        old_style = 0;
        if ((*str)[0] == '0' && (*str)[1] == 'x') {
@@ -80,44 +83,54 @@ blacklist_busid(char **str, int *id0, int *id1, int *devno)
                old_style = 1;
        }
        if (!isxdigit((*str)[0]))       /* We require at least one hex digit */
-               return -EINVAL;
+               goto confused;
        val = simple_strtoul(*str, str, 16);
        if (old_style || (*str)[0] != '.') {
                *id0 = *id1 = 0;
                if (val < 0 || val > 0xffff)
-                       return -EINVAL;
+                       goto confused;
                *devno = val;
+               if ((*str)[0] != ',' && (*str)[0] != '-' &&
+                   (*str)[0] != '\n' && (*str)[0] != '\0')
+                       goto confused;
                return 0;
        }
        /* New style x.y.z busid */
        if (val < 0 || val > 0xff)
-               return -EINVAL;
+               goto confused;
        *id0 = val;
        (*str)++;
        if (!isxdigit((*str)[0]))       /* We require at least one hex digit */
-               return -EINVAL;
+               goto confused;
        val = simple_strtoul(*str, str, 16);
        if (val < 0 || val > 0xff || (*str)++[0] != '.')
-               return -EINVAL;
+               goto confused;
        *id1 = val;
        if (!isxdigit((*str)[0]))       /* We require at least one hex digit */
-               return -EINVAL;
+               goto confused;
        val = simple_strtoul(*str, str, 16);
        if (val < 0 || val > 0xffff)
-               return -EINVAL;
+               goto confused;
        *devno = val;
+       if ((*str)[0] != ',' && (*str)[0] != '-' &&
+           (*str)[0] != '\n' && (*str)[0] != '\0')
+               goto confused;
        return 0;
+confused:
+       strsep(str, ",\n");
+       printk(KERN_WARNING "Invalid cio_ignore parameter '%s'\n", sav);
+       return 1;
 }
 
 static inline int
 blacklist_parse_parameters (char *str, range_action action)
 {
        unsigned int from, to, from_id0, to_id0, from_id1, to_id1;
-       char *sav;
 
-       sav = str;
        while (*str != 0 && *str != '\n') {
                range_action ra = action;
+               while(*str == ',')
+                       str++;
                if (*str == '!') {
                        ra = !action;
                        ++str;
@@ -138,32 +151,37 @@ blacklist_parse_parameters (char *str, range_action action)
                        rc = blacklist_busid(&str, &from_id0,
                                             &from_id1, &from);
                        if (rc)
-                               goto out_err;
+                               continue;
                        to = from;
                        to_id0 = from_id0;
                        to_id1 = from_id1;
                        if (*str == '-') {
                                str++;
-                               rc = blacklist_busid(&str, &to_id0, &to_id1,
-                                                    &to);
+                               rc = blacklist_busid(&str, &to_id0,
+                                                    &to_id1, &to);
                                if (rc)
-                                       goto out_err;
+                                       continue;
+                       }
+                       if (*str == '-') {
+                               printk(KERN_WARNING "invalid cio_ignore "
+                                       "parameter '%s'\n",
+                                       strsep(&str, ",\n"));
+                               continue;
+                       }
+                       if ((from_id0 != to_id0) || (from_id1 != to_id1)) {
+                               printk(KERN_WARNING "invalid cio_ignore range "
+                                       "%x.%x.%04x-%x.%x.%04x\n",
+                                       from_id0, from_id1, from,
+                                       to_id0, to_id1, to);
+                               continue;
                        }
-                       if ((from_id0 != to_id0) || (from_id1 != to_id1))
-                               goto out_err;
                }
                /* FIXME: ignoring id0 and id1 here. */
                pr_debug("blacklist_setup: adding range "
                         "from 0.0.%04x to 0.0.%04x\n", from, to);
                blacklist_range (ra, from, to);
-
-               if (*str == ',')
-                       str++;
        }
        return 1;
-out_err:
-       printk(KERN_WARNING "blacklist_setup: error parsing \"%s\"\n", sav);
-       return 0;
 }
 
 /* Parsing the commandline for blacklist parameters, e.g. to blacklist
index 2c22f6c503de5cb474048bab73dc59b6953729d4..54beb1340336c7a761b0a62767043d9027df83a9 100644 (file)
@@ -82,6 +82,7 @@ struct ccw_device_private {
                unsigned int dosense:1;     /* delayed SENSE required */
                unsigned int doverify:1;    /* delayed path verification */
                unsigned int donotify:1;    /* call notify function */
+               unsigned int recog_done:1;  /* dev. recog. complete */
        } __attribute__((packed)) flags;
        unsigned long intparm;  /* user interruption parameter */
        struct qdio_irq *qdio_data;
index 0cc958b5bc511560048399a54a691b981e4f571a..17c7785fac5526f7cc685cdab61431bcd7ec1217 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/device.c
  *  bus driver for ccw devices
- *   $Revision: 1.107 $
+ *   $Revision: 1.110 $
  *
  *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
  *                      IBM Corporation
@@ -263,10 +263,10 @@ ccw_device_set_offline(struct ccw_device *cdev)
 
        if (!cdev)
                return -ENODEV;
-       if (!cdev->online)
+       if (!cdev->online || !cdev->drv)
                return -EINVAL;
 
-       if (cdev->drv && cdev->drv->set_offline) {
+       if (cdev->drv->set_offline) {
                ret = cdev->drv->set_offline(cdev);
                if (ret != 0)
                        return ret;
@@ -292,7 +292,7 @@ ccw_device_set_online(struct ccw_device *cdev)
 
        if (!cdev)
                return -ENODEV;
-       if (cdev->online)
+       if (cdev->online || !cdev->drv)
                return -EINVAL;
 
        spin_lock_irq(cdev->ccwlock);
@@ -307,8 +307,7 @@ ccw_device_set_online(struct ccw_device *cdev)
        }
        if (cdev->private->state != DEV_STATE_ONLINE)
                return -ENODEV;
-       if (!cdev->drv || !cdev->drv->set_online ||
-           cdev->drv->set_online(cdev) == 0) {
+       if (!cdev->drv->set_online || cdev->drv->set_online(cdev) == 0) {
                cdev->online = 1;
                return 0;
        }
@@ -327,7 +326,7 @@ static ssize_t
 online_store (struct device *dev, const char *buf, size_t count)
 {
        struct ccw_device *cdev = to_ccwdev(dev);
-       int i, force;
+       int i, force, ret;
        char *tmp;
 
        if (atomic_compare_and_swap(0, 1, &cdev->private->onoff))
@@ -347,29 +346,46 @@ online_store (struct device *dev, const char *buf, size_t count)
        if (i == 1) {
                /* Do device recognition, if needed. */
                if (cdev->id.cu_type == 0) {
-                       ccw_device_recognition(cdev);
+                       ret = ccw_device_recognition(cdev);
+                       if (ret) {
+                               printk(KERN_WARNING"Couldn't start recognition "
+                                      "for device %s (ret=%d)\n",
+                                      cdev->dev.bus_id, ret);
+                               goto out;
+                       }
                        wait_event(cdev->private->wait_q,
-                                  dev_fsm_final_state(cdev));
+                                  cdev->private->flags.recog_done);
                }
-               ccw_device_set_online(cdev);
+               if (cdev->drv && cdev->drv->set_online)
+                       ccw_device_set_online(cdev);
        } else if (i == 0) {
                if (cdev->private->state == DEV_STATE_DISCONNECTED)
                        ccw_device_remove_disconnected(cdev);
-               else
+               else if (cdev->drv && cdev->drv->set_offline)
                        ccw_device_set_offline(cdev);
        }
        if (force && cdev->private->state == DEV_STATE_BOXED) {
-               int ret;
                ret = ccw_device_stlck(cdev);
-               if (ret)
+               if (ret) {
+                       printk(KERN_WARNING"ccw_device_stlck for device %s "
+                              "returned %d!\n", cdev->dev.bus_id, ret);
                        goto out;
+               }
                /* Do device recognition, if needed. */
                if (cdev->id.cu_type == 0) {
-                       ccw_device_recognition(cdev);
+                       cdev->private->state = DEV_STATE_NOT_OPER;
+                       ret = ccw_device_recognition(cdev);
+                       if (ret) {
+                               printk(KERN_WARNING"Couldn't start recognition "
+                                      "for device %s (ret=%d)\n",
+                                      cdev->dev.bus_id, ret);
+                               goto out;
+                       }
                        wait_event(cdev->private->wait_q,
-                                  dev_fsm_final_state(cdev));
+                                  cdev->private->flags.recog_done);
                }
-               ccw_device_set_online(cdev);
+               if (cdev->drv && cdev->drv->set_online)
+                       ccw_device_set_online(cdev);
        }
        out:
        if (cdev->drv)
@@ -530,7 +546,9 @@ io_subchannel_register(void *data)
                       __func__, sch->dev.bus_id);
        put_device(&cdev->dev);
 out:
+       cdev->private->flags.recog_done = 1;
        put_device(&sch->dev);
+       wake_up(&cdev->private->wait_q);
 }
 
 static void
@@ -555,10 +573,13 @@ io_subchannel_recog_done(struct ccw_device *cdev)
 {
        struct subchannel *sch;
 
-       if (css_init_done == 0)
+       if (css_init_done == 0) {
+               cdev->private->flags.recog_done = 1;
                return;
+       }
        switch (cdev->private->state) {
        case DEV_STATE_NOT_OPER:
+               cdev->private->flags.recog_done = 1;
                /* Remove device found not operational. */
                if (!get_device(&cdev->dev))
                        break;
index 989027b460ac4eefba9ac0831ad94a258929268f..a925c9b035e4ab71479b1328bf6b03f81386b291 100644 (file)
@@ -148,6 +148,7 @@ ccw_device_handle_oper(struct ccw_device *cdev)
        struct subchannel *sch;
 
        sch = to_subchannel(cdev->dev.parent);
+       cdev->private->flags.recog_done = 1;
        /*
         * Check if cu type and device type still match. If
         * not, it is certainly another device and we have to
@@ -217,6 +218,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
                __recover_lost_chpids(sch, old_lpm);
        if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) {
                if (state == DEV_STATE_NOT_OPER) {
+                       cdev->private->flags.recog_done = 1;
                        cdev->private->state = DEV_STATE_DISCONNECTED;
                        return;
                }
@@ -393,6 +395,7 @@ ccw_device_recognition(struct ccw_device *cdev)
         * timeout (or if sense pgid during path verification detects the device
         * is locked, as may happen on newer devices).
         */
+       cdev->private->flags.recog_done = 0;
        cdev->private->state = DEV_STATE_SENSE_ID;
        ccw_device_sense_id_start(cdev);
        return 0;
index 106191dfaf32bb648089367a11a22a5d9a3d7eb7..84dc9bb54748fae7b38acd52cbf21bcf184a5086 100644 (file)
@@ -79,7 +79,8 @@ ccw_device_start(struct ccw_device *cdev, struct ccw1 *cpa,
        if (cdev->private->state == DEV_STATE_NOT_OPER)
                return -ENODEV;
        if (cdev->private->state != DEV_STATE_ONLINE ||
-           sch->schib.scsw.actl != 0 ||
+           ((sch->schib.scsw.stctl & SCSW_STCTL_PRIM_STATUS) &&
+            !(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) ||
            cdev->private->flags.doverify)
                return -EBUSY;
        ret = cio_set_options (sch, flags);
@@ -347,7 +348,9 @@ read_dev_chars (struct ccw_device *cdev, void **buffer, int length)
        cdev->handler = ccw_device_wake_up;
        if (cdev->private->state != DEV_STATE_ONLINE)
                ret = -ENODEV;
-       else if (sch->schib.scsw.actl != 0 || cdev->private->flags.doverify)
+       else if (((sch->schib.scsw.stctl & SCSW_STCTL_PRIM_STATUS) &&
+                 !(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) ||
+                cdev->private->flags.doverify)
                ret = -EBUSY;
        else
                /* 0x00D9C4C3 == ebcdic "RDC" */
@@ -414,7 +417,9 @@ read_conf_data (struct ccw_device *cdev, void **buffer, int *length)
        cdev->handler = ccw_device_wake_up;
        if (cdev->private->state != DEV_STATE_ONLINE)
                ret = -ENODEV;
-       else if (sch->schib.scsw.actl != 0 || cdev->private->flags.doverify)
+       else if (((sch->schib.scsw.stctl & SCSW_STCTL_PRIM_STATUS) &&
+                 !(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) ||
+                cdev->private->flags.doverify)
                ret = -EBUSY;
        else
                /* 0x00D9C3C4 == ebcdic "RCD" */
@@ -441,12 +446,12 @@ read_conf_data (struct ccw_device *cdev, void **buffer, int *length)
 }
 
 /*
- * Try to issue an unconditional reserve on a boxed device.
+ * Try to break the lock on a boxed device.
  */
 int
 ccw_device_stlck(struct ccw_device *cdev)
 {
-       char buf[32];
+       void *buf, *buf2;
        unsigned long flags;
        struct subchannel *sch;
        int ret;
@@ -462,16 +467,30 @@ ccw_device_stlck(struct ccw_device *cdev)
        CIO_TRACE_EVENT(2, "stl lock");
        CIO_TRACE_EVENT(2, cdev->dev.bus_id);
 
+       buf = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       buf2 = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL);
+       if (!buf2) {
+               kfree(buf);
+               return -ENOMEM;
+       }
        spin_lock_irqsave(&sch->lock, flags);
        ret = cio_enable_subchannel(sch, 3);
        if (ret)
                goto out_unlock;
-       /* Setup ccw. This cmd code seems not to be in use elsewhere. */
+       /*
+        * Setup ccw. We chain an unconditional reserve and a release so we
+        * only break the lock.
+        */
        cdev->private->iccws[0].cmd_code = CCW_CMD_STLCK;
        cdev->private->iccws[0].cda = (__u32) __pa(buf);
        cdev->private->iccws[0].count = 32;
-       cdev->private->iccws[0].flags = CCW_FLAG_SLI;
-
+       cdev->private->iccws[0].flags = CCW_FLAG_CC;
+       cdev->private->iccws[1].cmd_code = CCW_CMD_RELEASE;
+       cdev->private->iccws[1].cda = (__u32) __pa(buf2);
+       cdev->private->iccws[1].count = 32;
+       cdev->private->iccws[1].flags = 0;
        ret = cio_start(sch, cdev->private->iccws, 0);
        if (ret) {
                cio_disable_subchannel(sch); //FIXME: return code?
@@ -486,10 +505,13 @@ ccw_device_stlck(struct ccw_device *cdev)
             (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
            (cdev->private->irb.scsw.cstat != 0))
                ret = -EIO;
-
        /* Clear irb. */
        memset(&cdev->private->irb, 0, sizeof(struct irb));
 out_unlock:
+       if (buf)
+               kfree(buf);
+       if (buf2)
+               kfree(buf2);
        spin_unlock_irqrestore(&sch->lock, flags);
        return ret;
 }
index 8521c178ecce037e3ca8d2d2281ca05451a3c106..c345e694df258eb213091df6d25a87194c1dae13 100644 (file)
@@ -99,7 +99,7 @@ ccw_device_accumulate_ecw(struct ccw_device *cdev, struct irb *irb)
 static inline int
 ccw_device_accumulate_esw_valid(struct irb *irb)
 {
-       if (irb->scsw.eswf && irb->scsw.stctl == SCSW_STCTL_STATUS_PEND)
+       if (!irb->scsw.eswf && irb->scsw.stctl == SCSW_STCTL_STATUS_PEND)
                return 0;
        if (irb->scsw.stctl == 
                        (SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND) &&
index cee14ac747aa0a9ef90a09025ab8d4f9a2e21558..d4e88181d1204b39cf8d04deff55b302501f19a7 100644 (file)
@@ -56,7 +56,7 @@
 #include "ioasm.h"
 #include "chsc.h"
 
-#define VERSION_QDIO_C "$Revision: 1.74 $"
+#define VERSION_QDIO_C "$Revision: 1.78 $"
 
 /****************** MODULE PARAMETER VARIABLES ********************/
 MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
@@ -545,6 +545,7 @@ inline static void
 qdio_kick_outbound_q(struct qdio_q *q)
 {
        int result;
+       char dbf_text[15];
 
        QDIO_DBF_TEXT4(0,trace,"kickoutq");
        QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
@@ -552,15 +553,75 @@ qdio_kick_outbound_q(struct qdio_q *q)
        if (!q->siga_out)
                return;
 
-       result=qdio_siga_output(q);
+       /* here's the story with cc=2 and busy bit set (thanks, Rick):
+        * VM's CP could present us cc=2 and busy bit set on SIGA-write
+        * during reconfiguration of their Guest LAN (only in HIPERS mode,
+        * QDIO mode is asynchronous -- cc=2 and busy bit there will take
+        * the queues down immediately; and not being under VM we have a
+        * problem on cc=2 and busy bit set right away).
+        *
+        * Therefore qdio_siga_output will try for a short time constantly,
+        * if such a condition occurs. If it doesn't change, it will
+        * increase the busy_siga_counter and save the timestamp, and
+        * schedule the queue for later processing (via mark_q, using the
+        * queue tasklet). __qdio_outbound_processing will check out the
+        * counter. If non-zero, it will call qdio_kick_outbound_q as often
+        * as the value of the counter. This will attempt further SIGA
+        * instructions. For each successful SIGA, the counter is
+        * decreased, for failing SIGAs the counter remains the same, after
+        * all.
+        * After some time of no movement, qdio_kick_outbound_q will
+        * finally fail and reflect corresponding error codes to call
+        * the upper layer module and have it take the queues down.
+        *
+        * Note that this is a change from the original HiperSockets design
+        * (saying cc=2 and busy bit means take the queues down), but in
+        * these days Guest LAN didn't exist... excessive cc=2 with busy bit
+        * conditions will still take the queues down, but the threshold is
+        * higher due to the Guest LAN environment.
+        */
 
-       if (!result)
-               return;
 
-       if (q->siga_error)
-               q->error_status_flags|=QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR;
-       q->error_status_flags |= QDIO_STATUS_LOOK_FOR_ERROR;
-       q->siga_error=result;
+       result=qdio_siga_output(q);
+
+               switch (result) {
+               case 0:
+               /* went smooth this time, reset timestamp */
+                       QDIO_DBF_TEXT3(0,trace,"cc2reslv");
+                       sprintf(dbf_text,"%4x%2x%2x",q->irq,q->q_no,
+                               atomic_read(&q->busy_siga_counter));
+                       QDIO_DBF_TEXT3(0,trace,dbf_text);
+                       q->timing.busy_start=0;
+                       break;
+               case (2|QDIO_SIGA_ERROR_B_BIT_SET):
+                       /* cc=2 and busy bit: */
+               atomic_inc(&q->busy_siga_counter);
+
+                       /* if the last siga was successful, save
+                        * timestamp here */
+                       if (!q->timing.busy_start)
+                               q->timing.busy_start=NOW;
+
+                       /* if we're in time, don't touch error_status_flags
+                        * and siga_error */
+                       if (NOW-q->timing.busy_start<QDIO_BUSY_BIT_GIVE_UP) {
+                               qdio_mark_q(q);
+                               break;
+                       }
+                       QDIO_DBF_TEXT2(0,trace,"cc2REPRT");
+                       sprintf(dbf_text,"%4x%2x%2x",q->irq,q->q_no,
+                               atomic_read(&q->busy_siga_counter));
+                       QDIO_DBF_TEXT3(0,trace,dbf_text);
+                       /* else fallthrough and report error */
+               default:
+                       /* for plain cc=1, 2 or 3: */
+                       if (q->siga_error)
+                               q->error_status_flags|=
+                                       QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR;
+                       q->error_status_flags|=
+                               QDIO_STATUS_LOOK_FOR_ERROR;
+                       q->siga_error=result;
+               }
 }
 
 inline static void
@@ -599,6 +660,8 @@ qdio_kick_outbound_handler(struct qdio_q *q)
 static inline void
 __qdio_outbound_processing(struct qdio_q *q)
 {
+       int siga_attempts;
+
        QDIO_DBF_TEXT4(0,trace,"qoutproc");
        QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
 
@@ -619,6 +682,14 @@ __qdio_outbound_processing(struct qdio_q *q)
        perf_stats.tl_runs++;
 #endif /* QDIO_PERFORMANCE_STATS */
 
+       /* see comment in qdio_kick_outbound_q */
+       siga_attempts=atomic_read(&q->busy_siga_counter);
+       while (siga_attempts) {
+               atomic_dec(&q->busy_siga_counter);
+               qdio_kick_outbound_q(q);
+               siga_attempts--;
+       }
+
        if (qdio_has_outbound_q_moved(q))
                qdio_kick_outbound_handler(q);
 
@@ -1368,6 +1439,10 @@ qdio_fill_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev,
                        ((irq_ptr->is_thinint_irq)?&tiqdio_inbound_processing:
                         &qdio_inbound_processing);
 
+               /* actually this is not used for inbound queues. yet. */
+               atomic_set(&q->busy_siga_counter,0);
+               q->timing.busy_start=0;
+
 /*             for (j=0;j<QDIO_STATS_NUMBER;j++)
                        q->timing.last_transfer_times[j]=(qdio_get_micros()/
                                                          QDIO_STATS_NUMBER)*j;
@@ -1432,6 +1507,9 @@ qdio_fill_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev,
                q->tasklet.func=(void(*)(unsigned long))
                        &qdio_outbound_processing;
 
+               atomic_set(&q->busy_siga_counter,0);
+               q->timing.busy_start=0;
+
                /* fill in slib */
                if (i>0) irq_ptr->output_qs[i-1]->slib->nsliba=
                                 (unsigned long)(q->slib);
@@ -2134,7 +2212,7 @@ qdio_cleanup(struct ccw_device *cdev, int how)
        QDIO_DBF_TEXT0(0,setup,dbf_text);
 
        rc = qdio_shutdown(cdev, how);
-       if (rc == 0)
+       if ((rc == 0) || (rc == -EINPROGRESS))
                rc = qdio_free(cdev);
        return rc;
 }
@@ -2145,6 +2223,7 @@ qdio_shutdown(struct ccw_device *cdev, int how)
        struct qdio_irq *irq_ptr;
        int i;
        int result = 0;
+       int rc;
        unsigned long flags;
        int timeout;
        char dbf_text[15];
@@ -2191,27 +2270,23 @@ qdio_shutdown(struct ccw_device *cdev, int how)
                        result=-EINPROGRESS;
        }
 
-       if (result)
-               goto out;
-
        /* cleanup subchannel */
        spin_lock_irqsave(get_ccwdev_lock(cdev),flags);
        if (how&QDIO_FLAG_CLEANUP_USING_CLEAR) {
-               result = ccw_device_clear(cdev, QDIO_DOING_CLEANUP);
+               rc = ccw_device_clear(cdev, QDIO_DOING_CLEANUP);
                timeout=QDIO_CLEANUP_CLEAR_TIMEOUT;
        } else if (how&QDIO_FLAG_CLEANUP_USING_HALT) {
-               result = ccw_device_halt(cdev, QDIO_DOING_CLEANUP);
+               rc = ccw_device_halt(cdev, QDIO_DOING_CLEANUP);
                timeout=QDIO_CLEANUP_HALT_TIMEOUT;
        } else { /* default behaviour */
-               result = ccw_device_halt(cdev, QDIO_DOING_CLEANUP);
+               rc = ccw_device_halt(cdev, QDIO_DOING_CLEANUP);
                timeout=QDIO_CLEANUP_HALT_TIMEOUT;
        }
-       if (result == -ENODEV) {
+       if (rc == -ENODEV) {
                /* No need to wait for device no longer present. */
                qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
-               result = 0; /* No error. */
                spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
-       } else if (result == 0) {
+       } else if (rc == 0) {
                qdio_set_state(irq_ptr, QDIO_IRQ_STATE_CLEANUP);
                ccw_device_set_timeout(cdev, timeout);
                spin_unlock_irqrestore(get_ccwdev_lock(cdev),flags);
@@ -2223,6 +2298,7 @@ qdio_shutdown(struct ccw_device *cdev, int how)
                QDIO_PRINT_INFO("ccw_device_{halt,clear} returned %d for "
                                "device %s\n", result, cdev->dev.bus_id);
                spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+               result = rc;
                goto out;
        }
        if (irq_ptr->is_thinint_irq) {
index a9189e8936eff0b502299283d5292b1ff87ab7dc..0922d4dacf725909d8e5be57c8c79e867b36ee12 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef _CIO_QDIO_H
 #define _CIO_QDIO_H
 
-#define VERSION_CIO_QDIO_H "$Revision: 1.22 $"
+#define VERSION_CIO_QDIO_H "$Revision: 1.23 $"
 
 //#define QDIO_DBF_LIKE_HELL
 
@@ -33,7 +33,8 @@
 
 #define TIQDIO_THININT_ISC 3
 #define TIQDIO_DELAY_TARGET 0
-#define QDIO_BUSY_BIT_PATIENCE 2000 /* in microsecs */
+#define QDIO_BUSY_BIT_PATIENCE 100 /* in microsecs */
+#define QDIO_BUSY_BIT_GIVE_UP 10000000 /* 10 seconds */
 #define IQDIO_GLOBAL_LAPS 2 /* GLOBAL_LAPS are not used as we */
 #define IQDIO_GLOBAL_LAPS_INT 1 /* don't global summary */
 #define IQDIO_LOCAL_LAPS 4
@@ -599,7 +600,9 @@ struct qdio_q {
                int last_transfer_index; */
 
                __u64 last_transfer_time;
+               __u64 busy_start;
        } timing;
+       atomic_t busy_siga_counter;
         unsigned int queue_type;
 
        /* leave this member at the end. won't be cleared in qdio_fill_qs */
index 254e5a78f1bd0f5ca84801010417e016d001e155..16f8b3453a0b8ac5a98cffff064ac5bc085e95f7 100644 (file)
@@ -132,6 +132,7 @@ struct ccw1 {
 #define CCW_CMD_SENSE_PGID     0x34
 #define CCW_CMD_SUSPEND_RECONN 0x5B
 #define CCW_CMD_RDC            0x64
+#define CCW_CMD_RELEASE                0x94
 #define CCW_CMD_SET_PGID       0xAF
 #define CCW_CMD_SENSE_ID       0xE4
 #define CCW_CMD_DCTL           0xF3