Commit 5c1fa264 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] s390: common i/o layer.

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.
parent a1366931
...@@ -250,7 +250,7 @@ xpram shows up under sys/ as 'xpram'. ...@@ -250,7 +250,7 @@ xpram shows up under sys/ as 'xpram'.
----------- -----------
The netiucv driver creates an attribute 'connection' under 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. connection to the specified host.
Netiucv connections show up under devices/iucv/ as "netiucv<ifnum>". The interface Netiucv connections show up under devices/iucv/ as "netiucv<ifnum>". The interface
......
/* /*
* drivers/s390/cio/blacklist.c * drivers/s390/cio/blacklist.c
* S/390 common I/O routines -- blacklisting of specific devices * S/390 common I/O routines -- blacklisting of specific devices
* $Revision: 1.29 $ * $Revision: 1.31 $
* *
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -72,7 +72,10 @@ static inline int ...@@ -72,7 +72,10 @@ static inline int
blacklist_busid(char **str, int *id0, int *id1, int *devno) blacklist_busid(char **str, int *id0, int *id1, int *devno)
{ {
int val, old_style; int val, old_style;
char *sav;
sav = *str;
/* check for leading '0x' */ /* check for leading '0x' */
old_style = 0; old_style = 0;
if ((*str)[0] == '0' && (*str)[1] == 'x') { if ((*str)[0] == '0' && (*str)[1] == 'x') {
...@@ -80,44 +83,54 @@ blacklist_busid(char **str, int *id0, int *id1, int *devno) ...@@ -80,44 +83,54 @@ blacklist_busid(char **str, int *id0, int *id1, int *devno)
old_style = 1; old_style = 1;
} }
if (!isxdigit((*str)[0])) /* We require at least one hex digit */ if (!isxdigit((*str)[0])) /* We require at least one hex digit */
return -EINVAL; goto confused;
val = simple_strtoul(*str, str, 16); val = simple_strtoul(*str, str, 16);
if (old_style || (*str)[0] != '.') { if (old_style || (*str)[0] != '.') {
*id0 = *id1 = 0; *id0 = *id1 = 0;
if (val < 0 || val > 0xffff) if (val < 0 || val > 0xffff)
return -EINVAL; goto confused;
*devno = val; *devno = val;
if ((*str)[0] != ',' && (*str)[0] != '-' &&
(*str)[0] != '\n' && (*str)[0] != '\0')
goto confused;
return 0; return 0;
} }
/* New style x.y.z busid */ /* New style x.y.z busid */
if (val < 0 || val > 0xff) if (val < 0 || val > 0xff)
return -EINVAL; goto confused;
*id0 = val; *id0 = val;
(*str)++; (*str)++;
if (!isxdigit((*str)[0])) /* We require at least one hex digit */ if (!isxdigit((*str)[0])) /* We require at least one hex digit */
return -EINVAL; goto confused;
val = simple_strtoul(*str, str, 16); val = simple_strtoul(*str, str, 16);
if (val < 0 || val > 0xff || (*str)++[0] != '.') if (val < 0 || val > 0xff || (*str)++[0] != '.')
return -EINVAL; goto confused;
*id1 = val; *id1 = val;
if (!isxdigit((*str)[0])) /* We require at least one hex digit */ if (!isxdigit((*str)[0])) /* We require at least one hex digit */
return -EINVAL; goto confused;
val = simple_strtoul(*str, str, 16); val = simple_strtoul(*str, str, 16);
if (val < 0 || val > 0xffff) if (val < 0 || val > 0xffff)
return -EINVAL; goto confused;
*devno = val; *devno = val;
if ((*str)[0] != ',' && (*str)[0] != '-' &&
(*str)[0] != '\n' && (*str)[0] != '\0')
goto confused;
return 0; return 0;
confused:
strsep(str, ",\n");
printk(KERN_WARNING "Invalid cio_ignore parameter '%s'\n", sav);
return 1;
} }
static inline int static inline int
blacklist_parse_parameters (char *str, range_action action) blacklist_parse_parameters (char *str, range_action action)
{ {
unsigned int from, to, from_id0, to_id0, from_id1, to_id1; unsigned int from, to, from_id0, to_id0, from_id1, to_id1;
char *sav;
sav = str;
while (*str != 0 && *str != '\n') { while (*str != 0 && *str != '\n') {
range_action ra = action; range_action ra = action;
while(*str == ',')
str++;
if (*str == '!') { if (*str == '!') {
ra = !action; ra = !action;
++str; ++str;
...@@ -138,32 +151,37 @@ blacklist_parse_parameters (char *str, range_action action) ...@@ -138,32 +151,37 @@ blacklist_parse_parameters (char *str, range_action action)
rc = blacklist_busid(&str, &from_id0, rc = blacklist_busid(&str, &from_id0,
&from_id1, &from); &from_id1, &from);
if (rc) if (rc)
goto out_err; continue;
to = from; to = from;
to_id0 = from_id0; to_id0 = from_id0;
to_id1 = from_id1; to_id1 = from_id1;
if (*str == '-') { if (*str == '-') {
str++; str++;
rc = blacklist_busid(&str, &to_id0, &to_id1, rc = blacklist_busid(&str, &to_id0,
&to); &to_id1, &to);
if (rc) 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. */ /* FIXME: ignoring id0 and id1 here. */
pr_debug("blacklist_setup: adding range " pr_debug("blacklist_setup: adding range "
"from 0.0.%04x to 0.0.%04x\n", from, to); "from 0.0.%04x to 0.0.%04x\n", from, to);
blacklist_range (ra, from, to); blacklist_range (ra, from, to);
if (*str == ',')
str++;
} }
return 1; 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 /* Parsing the commandline for blacklist parameters, e.g. to blacklist
......
...@@ -82,6 +82,7 @@ struct ccw_device_private { ...@@ -82,6 +82,7 @@ struct ccw_device_private {
unsigned int dosense:1; /* delayed SENSE required */ unsigned int dosense:1; /* delayed SENSE required */
unsigned int doverify:1; /* delayed path verification */ unsigned int doverify:1; /* delayed path verification */
unsigned int donotify:1; /* call notify function */ unsigned int donotify:1; /* call notify function */
unsigned int recog_done:1; /* dev. recog. complete */
} __attribute__((packed)) flags; } __attribute__((packed)) flags;
unsigned long intparm; /* user interruption parameter */ unsigned long intparm; /* user interruption parameter */
struct qdio_irq *qdio_data; struct qdio_irq *qdio_data;
......
/* /*
* drivers/s390/cio/device.c * drivers/s390/cio/device.c
* bus driver for ccw devices * bus driver for ccw devices
* $Revision: 1.107 $ * $Revision: 1.110 $
* *
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -263,10 +263,10 @@ ccw_device_set_offline(struct ccw_device *cdev) ...@@ -263,10 +263,10 @@ ccw_device_set_offline(struct ccw_device *cdev)
if (!cdev) if (!cdev)
return -ENODEV; return -ENODEV;
if (!cdev->online) if (!cdev->online || !cdev->drv)
return -EINVAL; return -EINVAL;
if (cdev->drv && cdev->drv->set_offline) { if (cdev->drv->set_offline) {
ret = cdev->drv->set_offline(cdev); ret = cdev->drv->set_offline(cdev);
if (ret != 0) if (ret != 0)
return ret; return ret;
...@@ -292,7 +292,7 @@ ccw_device_set_online(struct ccw_device *cdev) ...@@ -292,7 +292,7 @@ ccw_device_set_online(struct ccw_device *cdev)
if (!cdev) if (!cdev)
return -ENODEV; return -ENODEV;
if (cdev->online) if (cdev->online || !cdev->drv)
return -EINVAL; return -EINVAL;
spin_lock_irq(cdev->ccwlock); spin_lock_irq(cdev->ccwlock);
...@@ -307,8 +307,7 @@ ccw_device_set_online(struct ccw_device *cdev) ...@@ -307,8 +307,7 @@ ccw_device_set_online(struct ccw_device *cdev)
} }
if (cdev->private->state != DEV_STATE_ONLINE) if (cdev->private->state != DEV_STATE_ONLINE)
return -ENODEV; return -ENODEV;
if (!cdev->drv || !cdev->drv->set_online || if (!cdev->drv->set_online || cdev->drv->set_online(cdev) == 0) {
cdev->drv->set_online(cdev) == 0) {
cdev->online = 1; cdev->online = 1;
return 0; return 0;
} }
...@@ -327,7 +326,7 @@ static ssize_t ...@@ -327,7 +326,7 @@ static ssize_t
online_store (struct device *dev, const char *buf, size_t count) online_store (struct device *dev, const char *buf, size_t count)
{ {
struct ccw_device *cdev = to_ccwdev(dev); struct ccw_device *cdev = to_ccwdev(dev);
int i, force; int i, force, ret;
char *tmp; char *tmp;
if (atomic_compare_and_swap(0, 1, &cdev->private->onoff)) 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) ...@@ -347,29 +346,46 @@ online_store (struct device *dev, const char *buf, size_t count)
if (i == 1) { if (i == 1) {
/* Do device recognition, if needed. */ /* Do device recognition, if needed. */
if (cdev->id.cu_type == 0) { 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, 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) { } else if (i == 0) {
if (cdev->private->state == DEV_STATE_DISCONNECTED) if (cdev->private->state == DEV_STATE_DISCONNECTED)
ccw_device_remove_disconnected(cdev); ccw_device_remove_disconnected(cdev);
else else if (cdev->drv && cdev->drv->set_offline)
ccw_device_set_offline(cdev); ccw_device_set_offline(cdev);
} }
if (force && cdev->private->state == DEV_STATE_BOXED) { if (force && cdev->private->state == DEV_STATE_BOXED) {
int ret;
ret = ccw_device_stlck(cdev); 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; goto out;
}
/* Do device recognition, if needed. */ /* Do device recognition, if needed. */
if (cdev->id.cu_type == 0) { 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, 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: out:
if (cdev->drv) if (cdev->drv)
...@@ -530,7 +546,9 @@ io_subchannel_register(void *data) ...@@ -530,7 +546,9 @@ io_subchannel_register(void *data)
__func__, sch->dev.bus_id); __func__, sch->dev.bus_id);
put_device(&cdev->dev); put_device(&cdev->dev);
out: out:
cdev->private->flags.recog_done = 1;
put_device(&sch->dev); put_device(&sch->dev);
wake_up(&cdev->private->wait_q);
} }
static void static void
...@@ -555,10 +573,13 @@ io_subchannel_recog_done(struct ccw_device *cdev) ...@@ -555,10 +573,13 @@ io_subchannel_recog_done(struct ccw_device *cdev)
{ {
struct subchannel *sch; struct subchannel *sch;
if (css_init_done == 0) if (css_init_done == 0) {
cdev->private->flags.recog_done = 1;
return; return;
}
switch (cdev->private->state) { switch (cdev->private->state) {
case DEV_STATE_NOT_OPER: case DEV_STATE_NOT_OPER:
cdev->private->flags.recog_done = 1;
/* Remove device found not operational. */ /* Remove device found not operational. */
if (!get_device(&cdev->dev)) if (!get_device(&cdev->dev))
break; break;
......
...@@ -148,6 +148,7 @@ ccw_device_handle_oper(struct ccw_device *cdev) ...@@ -148,6 +148,7 @@ ccw_device_handle_oper(struct ccw_device *cdev)
struct subchannel *sch; struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
cdev->private->flags.recog_done = 1;
/* /*
* Check if cu type and device type still match. If * Check if cu type and device type still match. If
* not, it is certainly another device and we have to * not, it is certainly another device and we have to
...@@ -217,6 +218,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) ...@@ -217,6 +218,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
__recover_lost_chpids(sch, old_lpm); __recover_lost_chpids(sch, old_lpm);
if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) { if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) {
if (state == DEV_STATE_NOT_OPER) { if (state == DEV_STATE_NOT_OPER) {
cdev->private->flags.recog_done = 1;
cdev->private->state = DEV_STATE_DISCONNECTED; cdev->private->state = DEV_STATE_DISCONNECTED;
return; return;
} }
...@@ -393,6 +395,7 @@ ccw_device_recognition(struct ccw_device *cdev) ...@@ -393,6 +395,7 @@ ccw_device_recognition(struct ccw_device *cdev)
* timeout (or if sense pgid during path verification detects the device * timeout (or if sense pgid during path verification detects the device
* is locked, as may happen on newer devices). * is locked, as may happen on newer devices).
*/ */
cdev->private->flags.recog_done = 0;
cdev->private->state = DEV_STATE_SENSE_ID; cdev->private->state = DEV_STATE_SENSE_ID;
ccw_device_sense_id_start(cdev); ccw_device_sense_id_start(cdev);
return 0; return 0;
......
...@@ -79,7 +79,8 @@ ccw_device_start(struct ccw_device *cdev, struct ccw1 *cpa, ...@@ -79,7 +79,8 @@ ccw_device_start(struct ccw_device *cdev, struct ccw1 *cpa,
if (cdev->private->state == DEV_STATE_NOT_OPER) if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV; return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE || 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) cdev->private->flags.doverify)
return -EBUSY; return -EBUSY;
ret = cio_set_options (sch, flags); ret = cio_set_options (sch, flags);
...@@ -347,7 +348,9 @@ read_dev_chars (struct ccw_device *cdev, void **buffer, int length) ...@@ -347,7 +348,9 @@ read_dev_chars (struct ccw_device *cdev, void **buffer, int length)
cdev->handler = ccw_device_wake_up; cdev->handler = ccw_device_wake_up;
if (cdev->private->state != DEV_STATE_ONLINE) if (cdev->private->state != DEV_STATE_ONLINE)
ret = -ENODEV; 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; ret = -EBUSY;
else else
/* 0x00D9C4C3 == ebcdic "RDC" */ /* 0x00D9C4C3 == ebcdic "RDC" */
...@@ -414,7 +417,9 @@ read_conf_data (struct ccw_device *cdev, void **buffer, int *length) ...@@ -414,7 +417,9 @@ read_conf_data (struct ccw_device *cdev, void **buffer, int *length)
cdev->handler = ccw_device_wake_up; cdev->handler = ccw_device_wake_up;
if (cdev->private->state != DEV_STATE_ONLINE) if (cdev->private->state != DEV_STATE_ONLINE)
ret = -ENODEV; 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; ret = -EBUSY;
else else
/* 0x00D9C3C4 == ebcdic "RCD" */ /* 0x00D9C3C4 == ebcdic "RCD" */
...@@ -441,12 +446,12 @@ read_conf_data (struct ccw_device *cdev, void **buffer, int *length) ...@@ -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 int
ccw_device_stlck(struct ccw_device *cdev) ccw_device_stlck(struct ccw_device *cdev)
{ {
char buf[32]; void *buf, *buf2;
unsigned long flags; unsigned long flags;
struct subchannel *sch; struct subchannel *sch;
int ret; int ret;
...@@ -462,16 +467,30 @@ ccw_device_stlck(struct ccw_device *cdev) ...@@ -462,16 +467,30 @@ ccw_device_stlck(struct ccw_device *cdev)
CIO_TRACE_EVENT(2, "stl lock"); CIO_TRACE_EVENT(2, "stl lock");
CIO_TRACE_EVENT(2, cdev->dev.bus_id); 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); spin_lock_irqsave(&sch->lock, flags);
ret = cio_enable_subchannel(sch, 3); ret = cio_enable_subchannel(sch, 3);
if (ret) if (ret)
goto out_unlock; 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].cmd_code = CCW_CMD_STLCK;
cdev->private->iccws[0].cda = (__u32) __pa(buf); cdev->private->iccws[0].cda = (__u32) __pa(buf);
cdev->private->iccws[0].count = 32; 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); ret = cio_start(sch, cdev->private->iccws, 0);
if (ret) { if (ret) {
cio_disable_subchannel(sch); //FIXME: return code? cio_disable_subchannel(sch); //FIXME: return code?
...@@ -486,10 +505,13 @@ ccw_device_stlck(struct ccw_device *cdev) ...@@ -486,10 +505,13 @@ ccw_device_stlck(struct ccw_device *cdev)
(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) || (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
(cdev->private->irb.scsw.cstat != 0)) (cdev->private->irb.scsw.cstat != 0))
ret = -EIO; ret = -EIO;
/* Clear irb. */ /* Clear irb. */
memset(&cdev->private->irb, 0, sizeof(struct irb)); memset(&cdev->private->irb, 0, sizeof(struct irb));
out_unlock: out_unlock:
if (buf)
kfree(buf);
if (buf2)
kfree(buf2);
spin_unlock_irqrestore(&sch->lock, flags); spin_unlock_irqrestore(&sch->lock, flags);
return ret; return ret;
} }
......
...@@ -99,7 +99,7 @@ ccw_device_accumulate_ecw(struct ccw_device *cdev, struct irb *irb) ...@@ -99,7 +99,7 @@ ccw_device_accumulate_ecw(struct ccw_device *cdev, struct irb *irb)
static inline int static inline int
ccw_device_accumulate_esw_valid(struct irb *irb) 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; return 0;
if (irb->scsw.stctl == if (irb->scsw.stctl ==
(SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND) && (SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND) &&
......
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
#include "ioasm.h" #include "ioasm.h"
#include "chsc.h" #include "chsc.h"
#define VERSION_QDIO_C "$Revision: 1.74 $" #define VERSION_QDIO_C "$Revision: 1.78 $"
/****************** MODULE PARAMETER VARIABLES ********************/ /****************** MODULE PARAMETER VARIABLES ********************/
MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>"); MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
...@@ -545,6 +545,7 @@ inline static void ...@@ -545,6 +545,7 @@ inline static void
qdio_kick_outbound_q(struct qdio_q *q) qdio_kick_outbound_q(struct qdio_q *q)
{ {
int result; int result;
char dbf_text[15];
QDIO_DBF_TEXT4(0,trace,"kickoutq"); QDIO_DBF_TEXT4(0,trace,"kickoutq");
QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
...@@ -552,15 +553,75 @@ qdio_kick_outbound_q(struct qdio_q *q) ...@@ -552,15 +553,75 @@ qdio_kick_outbound_q(struct qdio_q *q)
if (!q->siga_out) if (!q->siga_out)
return; 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) result=qdio_siga_output(q);
q->error_status_flags|=QDIO_STATUS_MORE_THAN_ONE_SIGA_ERROR;
q->error_status_flags |= QDIO_STATUS_LOOK_FOR_ERROR; switch (result) {
q->siga_error=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 inline static void
...@@ -599,6 +660,8 @@ qdio_kick_outbound_handler(struct qdio_q *q) ...@@ -599,6 +660,8 @@ qdio_kick_outbound_handler(struct qdio_q *q)
static inline void static inline void
__qdio_outbound_processing(struct qdio_q *q) __qdio_outbound_processing(struct qdio_q *q)
{ {
int siga_attempts;
QDIO_DBF_TEXT4(0,trace,"qoutproc"); QDIO_DBF_TEXT4(0,trace,"qoutproc");
QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*));
...@@ -619,6 +682,14 @@ __qdio_outbound_processing(struct qdio_q *q) ...@@ -619,6 +682,14 @@ __qdio_outbound_processing(struct qdio_q *q)
perf_stats.tl_runs++; perf_stats.tl_runs++;
#endif /* QDIO_PERFORMANCE_STATS */ #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)) if (qdio_has_outbound_q_moved(q))
qdio_kick_outbound_handler(q); qdio_kick_outbound_handler(q);
...@@ -1368,6 +1439,10 @@ qdio_fill_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev, ...@@ -1368,6 +1439,10 @@ qdio_fill_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev,
((irq_ptr->is_thinint_irq)?&tiqdio_inbound_processing: ((irq_ptr->is_thinint_irq)?&tiqdio_inbound_processing:
&qdio_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++) /* for (j=0;j<QDIO_STATS_NUMBER;j++)
q->timing.last_transfer_times[j]=(qdio_get_micros()/ q->timing.last_transfer_times[j]=(qdio_get_micros()/
QDIO_STATS_NUMBER)*j; QDIO_STATS_NUMBER)*j;
...@@ -1432,6 +1507,9 @@ qdio_fill_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev, ...@@ -1432,6 +1507,9 @@ qdio_fill_qs(struct qdio_irq *irq_ptr, struct ccw_device *cdev,
q->tasklet.func=(void(*)(unsigned long)) q->tasklet.func=(void(*)(unsigned long))
&qdio_outbound_processing; &qdio_outbound_processing;
atomic_set(&q->busy_siga_counter,0);
q->timing.busy_start=0;
/* fill in slib */ /* fill in slib */
if (i>0) irq_ptr->output_qs[i-1]->slib->nsliba= if (i>0) irq_ptr->output_qs[i-1]->slib->nsliba=
(unsigned long)(q->slib); (unsigned long)(q->slib);
...@@ -2134,7 +2212,7 @@ qdio_cleanup(struct ccw_device *cdev, int how) ...@@ -2134,7 +2212,7 @@ qdio_cleanup(struct ccw_device *cdev, int how)
QDIO_DBF_TEXT0(0,setup,dbf_text); QDIO_DBF_TEXT0(0,setup,dbf_text);
rc = qdio_shutdown(cdev, how); rc = qdio_shutdown(cdev, how);
if (rc == 0) if ((rc == 0) || (rc == -EINPROGRESS))
rc = qdio_free(cdev); rc = qdio_free(cdev);
return rc; return rc;
} }
...@@ -2145,6 +2223,7 @@ qdio_shutdown(struct ccw_device *cdev, int how) ...@@ -2145,6 +2223,7 @@ qdio_shutdown(struct ccw_device *cdev, int how)
struct qdio_irq *irq_ptr; struct qdio_irq *irq_ptr;
int i; int i;
int result = 0; int result = 0;
int rc;
unsigned long flags; unsigned long flags;
int timeout; int timeout;
char dbf_text[15]; char dbf_text[15];
...@@ -2191,27 +2270,23 @@ qdio_shutdown(struct ccw_device *cdev, int how) ...@@ -2191,27 +2270,23 @@ qdio_shutdown(struct ccw_device *cdev, int how)
result=-EINPROGRESS; result=-EINPROGRESS;
} }
if (result)
goto out;
/* cleanup subchannel */ /* cleanup subchannel */
spin_lock_irqsave(get_ccwdev_lock(cdev),flags); spin_lock_irqsave(get_ccwdev_lock(cdev),flags);
if (how&QDIO_FLAG_CLEANUP_USING_CLEAR) { 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; timeout=QDIO_CLEANUP_CLEAR_TIMEOUT;
} else if (how&QDIO_FLAG_CLEANUP_USING_HALT) { } 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; timeout=QDIO_CLEANUP_HALT_TIMEOUT;
} else { /* default behaviour */ } else { /* default behaviour */
result = ccw_device_halt(cdev, QDIO_DOING_CLEANUP); rc = ccw_device_halt(cdev, QDIO_DOING_CLEANUP);
timeout=QDIO_CLEANUP_HALT_TIMEOUT; timeout=QDIO_CLEANUP_HALT_TIMEOUT;
} }
if (result == -ENODEV) { if (rc == -ENODEV) {
/* No need to wait for device no longer present. */ /* No need to wait for device no longer present. */
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
result = 0; /* No error. */
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); 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); qdio_set_state(irq_ptr, QDIO_IRQ_STATE_CLEANUP);
ccw_device_set_timeout(cdev, timeout); ccw_device_set_timeout(cdev, timeout);
spin_unlock_irqrestore(get_ccwdev_lock(cdev),flags); spin_unlock_irqrestore(get_ccwdev_lock(cdev),flags);
...@@ -2223,6 +2298,7 @@ qdio_shutdown(struct ccw_device *cdev, int how) ...@@ -2223,6 +2298,7 @@ qdio_shutdown(struct ccw_device *cdev, int how)
QDIO_PRINT_INFO("ccw_device_{halt,clear} returned %d for " QDIO_PRINT_INFO("ccw_device_{halt,clear} returned %d for "
"device %s\n", result, cdev->dev.bus_id); "device %s\n", result, cdev->dev.bus_id);
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
result = rc;
goto out; goto out;
} }
if (irq_ptr->is_thinint_irq) { if (irq_ptr->is_thinint_irq) {
......
#ifndef _CIO_QDIO_H #ifndef _CIO_QDIO_H
#define _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 //#define QDIO_DBF_LIKE_HELL
...@@ -33,7 +33,8 @@ ...@@ -33,7 +33,8 @@
#define TIQDIO_THININT_ISC 3 #define TIQDIO_THININT_ISC 3
#define TIQDIO_DELAY_TARGET 0 #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 2 /* GLOBAL_LAPS are not used as we */
#define IQDIO_GLOBAL_LAPS_INT 1 /* don't global summary */ #define IQDIO_GLOBAL_LAPS_INT 1 /* don't global summary */
#define IQDIO_LOCAL_LAPS 4 #define IQDIO_LOCAL_LAPS 4
...@@ -599,7 +600,9 @@ struct qdio_q { ...@@ -599,7 +600,9 @@ struct qdio_q {
int last_transfer_index; */ int last_transfer_index; */
__u64 last_transfer_time; __u64 last_transfer_time;
__u64 busy_start;
} timing; } timing;
atomic_t busy_siga_counter;
unsigned int queue_type; unsigned int queue_type;
/* leave this member at the end. won't be cleared in qdio_fill_qs */ /* leave this member at the end. won't be cleared in qdio_fill_qs */
......
...@@ -132,6 +132,7 @@ struct ccw1 { ...@@ -132,6 +132,7 @@ struct ccw1 {
#define CCW_CMD_SENSE_PGID 0x34 #define CCW_CMD_SENSE_PGID 0x34
#define CCW_CMD_SUSPEND_RECONN 0x5B #define CCW_CMD_SUSPEND_RECONN 0x5B
#define CCW_CMD_RDC 0x64 #define CCW_CMD_RDC 0x64
#define CCW_CMD_RELEASE 0x94
#define CCW_CMD_SET_PGID 0xAF #define CCW_CMD_SET_PGID 0xAF
#define CCW_CMD_SENSE_ID 0xE4 #define CCW_CMD_SENSE_ID 0xE4
#define CCW_CMD_DCTL 0xF3 #define CCW_CMD_DCTL 0xF3
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment