2004-03-12 19:40:30

by Martin Schwidefsky

[permalink] [raw]
Subject: [PATCH] s390 (2/10): common i/o layer.

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.

diffstat:
Documentation/s390/driver-model.txt | 2
drivers/s390/cio/blacklist.c | 64 +++++++++++++-------
drivers/s390/cio/css.h | 1
drivers/s390/cio/device.c | 55 ++++++++++++-----
drivers/s390/cio/device_fsm.c | 3
drivers/s390/cio/device_ops.c | 29 +++++++--
drivers/s390/cio/device_status.c | 2
drivers/s390/cio/qdio.c | 112 ++++++++++++++++++++++++++++++------
drivers/s390/cio/qdio.h | 7 +-
include/asm-s390/cio.h | 1
10 files changed, 208 insertions(+), 68 deletions(-)

diff -urN linux-2.6/Documentation/s390/driver-model.txt linux-2.6-s390/Documentation/s390/driver-model.txt
--- linux-2.6/Documentation/s390/driver-model.txt Thu Mar 11 03:55:20 2004
+++ linux-2.6-s390/Documentation/s390/driver-model.txt Fri Mar 12 20:03:31 2004
@@ -250,7 +250,7 @@
-----------

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
diff -urN linux-2.6/drivers/s390/cio/blacklist.c linux-2.6-s390/drivers/s390/cio/blacklist.c
--- linux-2.6/drivers/s390/cio/blacklist.c Thu Mar 11 03:55:24 2004
+++ linux-2.6-s390/drivers/s390/cio/blacklist.c Fri Mar 12 20:03:31 2004
@@ -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 @@
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 @@
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 @@
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
diff -urN linux-2.6/drivers/s390/cio/css.h linux-2.6-s390/drivers/s390/cio/css.h
--- linux-2.6/drivers/s390/cio/css.h Thu Mar 11 03:55:57 2004
+++ linux-2.6-s390/drivers/s390/cio/css.h Fri Mar 12 20:03:31 2004
@@ -82,6 +82,7 @@
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;
diff -urN linux-2.6/drivers/s390/cio/device.c linux-2.6-s390/drivers/s390/cio/device.c
--- linux-2.6/drivers/s390/cio/device.c Thu Mar 11 03:55:37 2004
+++ linux-2.6-s390/drivers/s390/cio/device.c Fri Mar 12 20:03:31 2004
@@ -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 @@

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 @@

if (!cdev)
return -ENODEV;
- if (cdev->online)
+ if (cdev->online || !cdev->drv)
return -EINVAL;

spin_lock_irq(cdev->ccwlock);
@@ -307,8 +307,7 @@
}
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 @@
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 @@
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 @@
__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 @@
{
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;
diff -urN linux-2.6/drivers/s390/cio/device_fsm.c linux-2.6-s390/drivers/s390/cio/device_fsm.c
--- linux-2.6/drivers/s390/cio/device_fsm.c Thu Mar 11 03:55:27 2004
+++ linux-2.6-s390/drivers/s390/cio/device_fsm.c Fri Mar 12 20:03:31 2004
@@ -148,6 +148,7 @@
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 @@
__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 @@
* 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;
diff -urN linux-2.6/drivers/s390/cio/device_ops.c linux-2.6-s390/drivers/s390/cio/device_ops.c
--- linux-2.6/drivers/s390/cio/device_ops.c Thu Mar 11 03:55:37 2004
+++ linux-2.6-s390/drivers/s390/cio/device_ops.c Fri Mar 12 20:03:31 2004
@@ -441,12 +441,12 @@
}

/*
- * 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 +462,30 @@
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 +500,13 @@
(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;
}
diff -urN linux-2.6/drivers/s390/cio/device_status.c linux-2.6-s390/drivers/s390/cio/device_status.c
--- linux-2.6/drivers/s390/cio/device_status.c Thu Mar 11 03:55:27 2004
+++ linux-2.6-s390/drivers/s390/cio/device_status.c Fri Mar 12 20:03:31 2004
@@ -99,7 +99,7 @@
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) &&
diff -urN linux-2.6/drivers/s390/cio/qdio.c linux-2.6-s390/drivers/s390/cio/qdio.c
--- linux-2.6/drivers/s390/cio/qdio.c Thu Mar 11 03:55:34 2004
+++ linux-2.6-s390/drivers/s390/cio/qdio.c Fri Mar 12 20:03:31 2004
@@ -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 <[email protected]>");
@@ -545,6 +545,7 @@
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 @@
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 @@
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 @@
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 @@
((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 @@
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_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 @@
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 @@
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_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) {
diff -urN linux-2.6/drivers/s390/cio/qdio.h linux-2.6-s390/drivers/s390/cio/qdio.h
--- linux-2.6/drivers/s390/cio/qdio.h Thu Mar 11 03:55:23 2004
+++ linux-2.6-s390/drivers/s390/cio/qdio.h Fri Mar 12 20:03:31 2004
@@ -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 @@
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 */
diff -urN linux-2.6/include/asm-s390/cio.h linux-2.6-s390/include/asm-s390/cio.h
--- linux-2.6/include/asm-s390/cio.h Thu Mar 11 03:55:44 2004
+++ linux-2.6-s390/include/asm-s390/cio.h Fri Mar 12 20:03:31 2004
@@ -132,6 +132,7 @@
#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