Commit be457375 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:
 - Add atomic onoff variable to ccw devices and ccw-group devices to
   avoid races during online/offline.
 - Fix pr_debug calls.
 - A lot of path fixes:
   + Set device to disconnected state after no path event.
   + Fix chpid vary on/off for single path devices.
   + Make logical vary on/off consistent with physical vary on/off.
   + Don't update subchannel schib if the device is gone (dnv not set).
   + Add code to recover lost chpids after machine checks.
   + Avoid processing link incidents, resource accessability events and
     chpid machine checks for logically offline chpids.
   + Recover disconnected devices after chsc machine checks.
   + Delay de-registering of no path devices to avoid deadlocks.
   + Don't redo ssd for known subchannels - the info is static.
   + Introduce a second, "slow" machine check handler thread for new devices.
     The "fast" machine check handler only recovers disconnected devices.
 - Deregister subchannel rather than ccw device on not oper events.
 - Fix calling sequence of notify function vs. path verification.
 - Reset timeout for disconnected devices.
 - Fix problem with debug feature and %s arguments.
 - Fix __get_subchannel_by_stsch to deal with "zombie" subchannels.
 - Avoid "zombie" subchannels if device is not operational during sense id.
 - Handle call to the io_subchannel remove function if the ccw device
   is not registered yet.
 - Add availability attribute for ccw devices: "good", "no device",
   "no path", "boxed".
 - Export ccw_device_work for qdio as module.
 - Retry sense id for tape devices which present intervention required.
 - Don't check the activity control to decide if the device driver interrupt
   handler needs to be called but use the bits in status control.
 - Fix race in ccw_device_stlck.
 - Accumulate deferred condition code.
 - Fix setting_up_sema locking.
 - Call qdio_shutdown instead of qdio_cleanup on failed establish.
 - Fix problem when 64 FCP adapters are initialized simultaneously.
 - Fix problem with >64 adapter interrupt capable devices.
 - Reduce stack usage in qdio.
parent b5f520b7
...@@ -8,23 +8,26 @@ Command line parameters ...@@ -8,23 +8,26 @@ Command line parameters
Determines whether information on found devices and sensed device Determines whether information on found devices and sensed device
characteristics should be shown during startup, i. e. messages of the types characteristics should be shown during startup, i. e. messages of the types
"Detected device 4711 on subchannel 42" and "SenseID: Device 4711 reports: ...". "Detected device 0.0.4711 on subchannel 0.0.0042" and "SenseID: Device
0.0.4711 reports: ...".
Default is off. Default is off.
* cio_notoper_msg = yes | no * cio_notoper_msg = yes | no
Determines whether messages of the type "Device 4711 became 'not operational'" Determines whether messages of the type "Device 0.0.4711 became 'not
should be shown during startup; after startup, they will always be shown. operational'" should be shown during startup; after startup, they will always
be shown.
Default is on. Default is on.
* cio_ignore = <device number> | <range of device numbers>, * cio_ignore = {all} |
<device number> | <range of device numbers>, ... {<device> | <range of devices>} |
{!<device> | !<range of devices>}
The given device numbers will be ignored by the common I/O-layer; no detection The given devices will be ignored by the common I/O-layer; no detection
and device sensing will be done on any of those devices. The subchannel to and device sensing will be done on any of those devices. The subchannel to
which the device in question is attached will be treated as if no device was which the device in question is attached will be treated as if no device was
attached. attached.
...@@ -32,12 +35,19 @@ Command line parameters ...@@ -32,12 +35,19 @@ Command line parameters
An ignored device can be un-ignored later; see the "/proc entries"-section for An ignored device can be un-ignored later; see the "/proc entries"-section for
details. details.
The device numbers must be given hexadecimal. The devices must be given either as bus ids (0.0.abcd) or as hexadecimal
device numbers (0xabcd or abcd, for 2.4 backward compatibility).
You can use the 'all' keyword to ignore all devices.
The '!' operator will cause the I/O-layer to _not_ ignore a device.
The order on the command line is not important.
For example, For example,
cio_ignore=0x23-0x42,0x4711 cio_ignore=0.0.0023-0.0.0042,0.0.4711
will ignore all devices with device numbers ranging from 23 to 42 and the will ignore all devices ranging from 0.0.0023 to 0.0.0042 and the device
device with device number 4711, if detected. 0.0.4711, if detected.
As another example,
cio_ignore=all,!0.0.4711,!0.0.fd00-0.0.fd02
will ignore all devices but 0.0.4711, 0.0.fd00, 0.0.fd01, 0.0.fd02.
By default, no devices are ignored. By default, no devices are ignored.
...@@ -47,17 +57,19 @@ Command line parameters ...@@ -47,17 +57,19 @@ Command line parameters
* /proc/cio_ignore * /proc/cio_ignore
Lists the ranges of device numbers which are ignored by common I/O. Lists the ranges of devices (by bus id) which are ignored by common I/O.
You can un-ignore certain or all devices by piping to /proc/cio_ignore. You can un-ignore certain or all devices by piping to /proc/cio_ignore.
"free all" will un-ignore all ignored devices, "free all" will un-ignore all ignored devices,
"free <devnorange>, <devnorange>, ..." will un-ignore the specified devices. "free <device range>, <device range>, ..." will un-ignore the specified
devices.
For example, if devices 23 to 42 and 4711 are ignored,
- echo free 0x30-0x32 > /proc/cio_ignore For example, if devices 0.0.0023 to 0.0.0042 and 0.0.4711 are ignored,
will un-ignore devices 30 to 32 and will leave devices 23 to 2F, 33 to 42 - echo free 0.0.0030-0.0.0032 > /proc/cio_ignore
and 4711 ignored; will un-ignore devices 0.0.0030 to 0.0.0032 and will leave devices 0.0.0023
- echo free 0x41 > /proc/cio_ignore will furthermore un-ignore device 41; to 0.0.002f, 0.0.0033 to 0.0.0042 and 0.0.4711 ignored;
- echo free 0.0.0041 > /proc/cio_ignore will furthermore un-ignore device
0.0.0041;
- echo free all > /proc/cio_ignore will un-ignore all remaining ignored - echo free all > /proc/cio_ignore will un-ignore all remaining ignored
devices. devices.
...@@ -66,15 +78,19 @@ Command line parameters ...@@ -66,15 +78,19 @@ Command line parameters
available to the system. available to the system.
You can also add ranges of devices to be ignored by piping to You can also add ranges of devices to be ignored by piping to
/proc/cio_ignore; "add <devnorange>, <devnorange>, ..." will ignore the /proc/cio_ignore; "add <device range>, <device range>, ..." will ignore the
specified devices. specified devices.
Note: Already known devices cannot be ignored; this also applies to devices Note: Already known devices cannot be ignored.
which are gone after a machine check.
For example, if device 0.0.abcd is already known and all other devices
0.0.a000-0.0.afff are not known,
"echo add 0.0.a000-0.0.accc, 0.0.af00-0.0.afff > /proc/cio_ignore"
will add 0.0.a000-0.0.abcc, 0.0.abce-0.0.accc and 0.0.af00-0.0.afff to the
list of ignored devices and skip 0.0.abcd.
For example, if device abcd is already known and all other devices a000-afff The devices can be specified either by bus id (0.0.abcd) or, for 2.4 backward
are not known, "echo add 0xa000-0xaccc, 0xaf00-0xafff > /proc/cio_ignore" compatibilty, by the device number in hexadecimal (0xabcd or abcd).
will add af00-afff to the list of ignored devices and skip a000-accc.
* /proc/s390dbf/cio_*/ (S/390 debug feature) * /proc/s390dbf/cio_*/ (S/390 debug feature)
......
...@@ -216,9 +216,17 @@ mind that most drivers will need to implement both a ccwgroup and a ccw driver ...@@ -216,9 +216,17 @@ mind that most drivers will need to implement both a ccwgroup and a ccw driver
Channel paths show up, like subchannels, under the channel subsystem root (css0) Channel paths show up, like subchannels, under the channel subsystem root (css0)
and are called 'chp0.<chpid>'. They have no driver and do not belong to any bus. and are called 'chp0.<chpid>'. They have no driver and do not belong to any bus.
Please note, that unlike /proc/chpids in 2.4, the channel path objects reflect
only the logical state and not the physical state, since we cannot track the
latter consistently due to lacking machine support (we don't need to be aware
of anyway).
status - Can be 'online', 'logically offline' or 'n/a'. status - Can be 'online' or 'offline'.
Piping 'on' or 'off' sets the chpid logically online/offline. Piping 'on' or 'off' sets the chpid logically online/offline.
Piping 'on' to an online chpid triggers path reprobing for all devices
the chpid connects to. This can be used to force the kernel to re-use
a channel path the user knows to be online, but the machine hasn't
created a machine check for.
3. System devices 3. System devices
......
/* /*
* 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.27 $ * $Revision: 1.29 $
* *
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -18,9 +18,11 @@ ...@@ -18,9 +18,11 @@
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/device.h> #include <linux/device.h>
#include <asm/cio.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "blacklist.h" #include "blacklist.h"
#include "cio.h"
#include "cio_debug.h" #include "cio_debug.h"
#include "css.h" #include "css.h"
...@@ -199,8 +201,6 @@ is_blacklisted (int devno) ...@@ -199,8 +201,6 @@ is_blacklisted (int devno)
} }
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
extern void css_reiterate_subchannels(void);
/* /*
* Function: s390_redo_validation * Function: s390_redo_validation
* Look for no longer blacklisted devices * Look for no longer blacklisted devices
...@@ -208,9 +208,29 @@ extern void css_reiterate_subchannels(void); ...@@ -208,9 +208,29 @@ extern void css_reiterate_subchannels(void);
static inline void static inline void
s390_redo_validation (void) s390_redo_validation (void)
{ {
CIO_TRACE_EVENT (0, "redoval"); unsigned int irq;
css_reiterate_subchannels(); CIO_TRACE_EVENT (0, "redoval");
for (irq = 0; irq <= __MAX_SUBCHANNELS; irq++) {
int ret;
struct subchannel *sch;
sch = get_subchannel_by_schid(irq);
if (sch) {
/* Already known. */
put_device(&sch->dev);
continue;
}
ret = css_probe_device(irq);
if (ret == -ENXIO)
break; /* We're through. */
if (ret == -ENOMEM)
/*
* Stop validation for now. Bad, but no need for a
* panic.
*/
break;
}
} }
/* /*
......
/* /*
* drivers/s390/cio/ccwgroup.c * drivers/s390/cio/ccwgroup.c
* bus driver for ccwgroup * bus driver for ccwgroup
* $Revision: 1.19 $ * $Revision: 1.23 $
* *
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -164,6 +164,7 @@ ccwgroup_create(struct device *root, ...@@ -164,6 +164,7 @@ ccwgroup_create(struct device *root,
return -ENOMEM; return -ENOMEM;
memset(gdev, 0, sizeof(*gdev) + argc*sizeof(gdev->cdev[0])); memset(gdev, 0, sizeof(*gdev) + argc*sizeof(gdev->cdev[0]));
atomic_set(&gdev->onoff, 0);
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]); gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]);
...@@ -242,18 +243,24 @@ ccwgroup_set_online(struct ccwgroup_device *gdev) ...@@ -242,18 +243,24 @@ ccwgroup_set_online(struct ccwgroup_device *gdev)
struct ccwgroup_driver *gdrv; struct ccwgroup_driver *gdrv;
int ret; int ret;
if (gdev->state == CCWGROUP_ONLINE) if (atomic_compare_and_swap(0, 1, &gdev->onoff))
return 0; return -EAGAIN;
if (gdev->state == CCWGROUP_ONLINE) {
if (!gdev->dev.driver) ret = 0;
return -EINVAL; goto out;
}
if (!gdev->dev.driver) {
ret = -EINVAL;
goto out;
}
gdrv = to_ccwgroupdrv (gdev->dev.driver); gdrv = to_ccwgroupdrv (gdev->dev.driver);
if ((ret = gdrv->set_online(gdev))) if ((ret = gdrv->set_online(gdev)))
return ret; goto out;
gdev->state = CCWGROUP_ONLINE; gdev->state = CCWGROUP_ONLINE;
return 0; out:
atomic_set(&gdev->onoff, 0);
return ret;
} }
static int static int
...@@ -262,18 +269,24 @@ ccwgroup_set_offline(struct ccwgroup_device *gdev) ...@@ -262,18 +269,24 @@ ccwgroup_set_offline(struct ccwgroup_device *gdev)
struct ccwgroup_driver *gdrv; struct ccwgroup_driver *gdrv;
int ret; int ret;
if (gdev->state == CCWGROUP_OFFLINE) if (atomic_compare_and_swap(0, 1, &gdev->onoff))
return 0; return -EAGAIN;
if (gdev->state == CCWGROUP_OFFLINE) {
if (!gdev->dev.driver) ret = 0;
return -EINVAL; goto out;
}
if (!gdev->dev.driver) {
ret = -EINVAL;
goto out;
}
gdrv = to_ccwgroupdrv (gdev->dev.driver); gdrv = to_ccwgroupdrv (gdev->dev.driver);
if ((ret = gdrv->set_offline(gdev))) if ((ret = gdrv->set_offline(gdev)))
return ret; goto out;
gdev->state = CCWGROUP_OFFLINE; gdev->state = CCWGROUP_OFFLINE;
return 0; out:
atomic_set(&gdev->onoff, 0);
return ret;
} }
static ssize_t static ssize_t
...@@ -324,7 +337,7 @@ ccwgroup_probe (struct device *dev) ...@@ -324,7 +337,7 @@ ccwgroup_probe (struct device *dev)
if ((ret = device_create_file(dev, &dev_attr_online))) if ((ret = device_create_file(dev, &dev_attr_online)))
return ret; return ret;
pr_debug("%s: device %s\n", __func__, gdev->dev.name); pr_debug("%s: device %s\n", __func__, gdev->dev.bus_id);
ret = gdrv->probe ? gdrv->probe(gdev) : -ENODEV; ret = gdrv->probe ? gdrv->probe(gdev) : -ENODEV;
if (ret) if (ret)
device_remove_file(dev, &dev_attr_online); device_remove_file(dev, &dev_attr_online);
...@@ -341,7 +354,7 @@ ccwgroup_remove (struct device *dev) ...@@ -341,7 +354,7 @@ ccwgroup_remove (struct device *dev)
gdev = to_ccwgroupdev(dev); gdev = to_ccwgroupdev(dev);
gdrv = to_ccwgroupdrv(dev->driver); gdrv = to_ccwgroupdrv(dev->driver);
pr_debug("%s: device %s\n", __func__, gdev->dev.name); pr_debug("%s: device %s\n", __func__, gdev->dev.bus_id);
device_remove_file(dev, &dev_attr_online); device_remove_file(dev, &dev_attr_online);
......
This diff is collapsed.
...@@ -3,10 +3,6 @@ ...@@ -3,10 +3,6 @@
#define NR_CHPIDS 256 #define NR_CHPIDS 256
#define CHP_STANDBY 1
#define CHP_LOGICALLY_OFFLINE 2
#define CHP_ONLINE 4
#define CHSC_SEI_ACC_CHPID 1 #define CHSC_SEI_ACC_CHPID 1
#define CHSC_SEI_ACC_LINKADDR 2 #define CHSC_SEI_ACC_LINKADDR 2
#define CHSC_SEI_ACC_FULLLINKADDR 3 #define CHSC_SEI_ACC_FULLLINKADDR 3
...@@ -18,10 +14,7 @@ struct chsc_header { ...@@ -18,10 +14,7 @@ struct chsc_header {
struct channel_path { struct channel_path {
int id; int id;
struct { int state;
unsigned int online:1;
unsigned int logically_online:1;
}__attribute__((packed)) state;
struct device dev; struct device dev;
}; };
...@@ -29,4 +22,6 @@ extern struct channel_path *chps[]; ...@@ -29,4 +22,6 @@ extern struct channel_path *chps[];
extern void s390_process_css( void ); extern void s390_process_css( void );
extern void chsc_validate_chpids(struct subchannel *); extern void chsc_validate_chpids(struct subchannel *);
extern void chpid_is_actually_online(int);
extern int is_chpid_online(int);
#endif #endif
/* /*
* drivers/s390/cio/cio.c * drivers/s390/cio/cio.c
* S/390 common I/O routines -- low level i/o calls * S/390 common I/O routines -- low level i/o calls
* $Revision: 1.114 $ * $Revision: 1.117 $
* *
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -173,7 +173,7 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm) ...@@ -173,7 +173,7 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
stsch (sch->irq, &sch->schib); stsch (sch->irq, &sch->schib);
CIO_MSG_EVENT(0, "cio_start: 'not oper' status for " CIO_MSG_EVENT(0, "cio_start: 'not oper' status for "
"subchannel %s!\n", sch->dev.bus_id); "subchannel %04x!\n", sch->irq);
sprintf(dbf_text, "no%s", sch->dev.bus_id); sprintf(dbf_text, "no%s", sch->dev.bus_id);
CIO_TRACE_EVENT(0, dbf_text); CIO_TRACE_EVENT(0, dbf_text);
CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib)); CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib));
...@@ -572,9 +572,9 @@ cio_validate_subchannel (struct subchannel *sch, unsigned int irq) ...@@ -572,9 +572,9 @@ cio_validate_subchannel (struct subchannel *sch, unsigned int irq)
sch->opm; sch->opm;
CIO_DEBUG(KERN_INFO, 0, CIO_DEBUG(KERN_INFO, 0,
"Detected device %04X on subchannel %s" "Detected device %04X on subchannel %04X"
" - PIM = %02X, PAM = %02X, POM = %02X\n", " - PIM = %02X, PAM = %02X, POM = %02X\n",
sch->schib.pmcw.dev, sch->dev.bus_id, sch->schib.pmcw.pim, sch->schib.pmcw.dev, sch->irq, sch->schib.pmcw.pim,
sch->schib.pmcw.pam, sch->schib.pmcw.pom); sch->schib.pmcw.pam, sch->schib.pmcw.pom);
/* /*
......
/* /*
* drivers/s390/cio/css.c * drivers/s390/cio/css.c
* driver for channel subsystem * driver for channel subsystem
* $Revision: 1.65 $ * $Revision: 1.69 $
* *
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/list.h>
#include "css.h" #include "css.h"
#include "cio.h" #include "cio.h"
...@@ -20,6 +21,7 @@ ...@@ -20,6 +21,7 @@
#include "ioasm.h" #include "ioasm.h"
unsigned int highest_subchannel; unsigned int highest_subchannel;
int need_rescan = 0;
int css_init_done = 0; int css_init_done = 0;
struct device css_bus_device = { struct device css_bus_device = {
...@@ -130,7 +132,7 @@ __get_subchannel_by_stsch(int irq) ...@@ -130,7 +132,7 @@ __get_subchannel_by_stsch(int irq)
struct schib schib; struct schib schib;
cc = stsch(irq, &schib); cc = stsch(irq, &schib);
if (cc) if (cc || !schib.pmcw.dnv)
return NULL; return NULL;
sch = (struct subchannel *)(unsigned long)schib.pmcw.intparm; sch = (struct subchannel *)(unsigned long)schib.pmcw.intparm;
if (!sch) if (!sch)
...@@ -154,15 +156,18 @@ get_subchannel_by_schid(int irq) ...@@ -154,15 +156,18 @@ get_subchannel_by_schid(int irq)
sch = __get_subchannel_by_stsch(irq); sch = __get_subchannel_by_stsch(irq);
if (sch) if (sch)
goto out; goto out;
if (!get_driver(&io_subchannel_driver.drv))
goto out;
down_read(&css_bus_type.subsys.rwsem); down_read(&css_bus_type.subsys.rwsem);
list_for_each(entry, &io_subchannel_driver.drv.devices) { list_for_each(entry, &css_bus_type.devices.list) {
dev = get_device(container_of(entry, dev = get_device(container_of(entry,
struct device, driver_list)); struct device, bus_list));
if (!dev) if (!dev)
continue; continue;
/* Skip channel paths. */
if (dev->release != &css_subchannel_release) {
put_device(dev);
continue;
}
sch = to_subchannel(dev); sch = to_subchannel(dev);
if (sch->irq == irq) if (sch->irq == irq)
break; break;
...@@ -170,7 +175,6 @@ get_subchannel_by_schid(int irq) ...@@ -170,7 +175,6 @@ get_subchannel_by_schid(int irq)
sch = NULL; sch = NULL;
} }
up_read(&css_bus_type.subsys.rwsem); up_read(&css_bus_type.subsys.rwsem);
put_driver(&io_subchannel_driver.drv);
out: out:
put_bus(&css_bus_type); put_bus(&css_bus_type);
...@@ -188,19 +192,24 @@ css_get_subchannel_status(struct subchannel *sch, int schid) ...@@ -188,19 +192,24 @@ css_get_subchannel_status(struct subchannel *sch, int schid)
return CIO_GONE; return CIO_GONE;
if (!schib.pmcw.dnv) if (!schib.pmcw.dnv)
return CIO_GONE; return CIO_GONE;
if (sch && (schib.pmcw.dev != sch->schib.pmcw.dev)) if (sch && sch->schib.pmcw.dnv &&
(schib.pmcw.dev != sch->schib.pmcw.dev))
return CIO_REVALIDATE; return CIO_REVALIDATE;
return CIO_OPER; return CIO_OPER;
} }
static inline int static inline int
css_evaluate_subchannel(int irq) css_evaluate_subchannel(int irq, int slow)
{ {
int event, ret, disc; int event, ret, disc;
struct subchannel *sch; struct subchannel *sch;
sch = get_subchannel_by_schid(irq); sch = get_subchannel_by_schid(irq);
disc = sch ? device_is_disconnected(sch) : 0; disc = sch ? device_is_disconnected(sch) : 0;
if (disc && slow)
return 0; /* Already processed. */
if (!disc && !slow)
return -EAGAIN; /* Will be done on the slow path. */
event = css_get_subchannel_status(sch, irq); event = css_get_subchannel_status(sch, irq);
switch (event) { switch (event) {
case CIO_GONE: case CIO_GONE:
...@@ -252,18 +261,13 @@ css_evaluate_subchannel(int irq) ...@@ -252,18 +261,13 @@ css_evaluate_subchannel(int irq)
return ret; return ret;
} }
/* static void
* Rescan for new devices. FIXME: This is slow. css_rescan_devices(void)
* This function is called when we have lost CRWs due to overflows and we have
* to do subchannel housekeeping.
*/
void
css_reiterate_subchannels(void)
{ {
int irq, ret; int irq, ret;
for (irq = 0; irq <= __MAX_SUBCHANNELS; irq++) { for (irq = 0; irq <= __MAX_SUBCHANNELS; irq++) {
ret = css_evaluate_subchannel(irq); ret = css_evaluate_subchannel(irq, 1);
/* No more memory. It doesn't make sense to continue. No /* No more memory. It doesn't make sense to continue. No
* panic because this can happen in midflight and just * panic because this can happen in midflight and just
* because we can't use a new device is no reason to crash * because we can't use a new device is no reason to crash
...@@ -276,20 +280,61 @@ css_reiterate_subchannels(void) ...@@ -276,20 +280,61 @@ css_reiterate_subchannels(void)
} }
} }
static void
css_evaluate_slow_subchannel(unsigned long schid)
{
css_evaluate_subchannel(schid, 1);
}
void
css_trigger_slow_path(void)
{
if (need_rescan) {
need_rescan = 0;
css_rescan_devices();
return;
}
css_walk_subchannel_slow_list(css_evaluate_slow_subchannel);
}
/* /*
* Called from the machine check handler for subchannel report words. * Rescan for new devices. FIXME: This is slow.
* This function is called when we have lost CRWs due to overflows and we have
* to do subchannel housekeeping.
*/ */
void void
css_reiterate_subchannels(void)
{
css_clear_subchannel_slow_list();
need_rescan = 1;
}
/*
* Called from the machine check handler for subchannel report words.
*/
int
css_process_crw(int irq) css_process_crw(int irq)
{ {
int ret;
CIO_CRW_EVENT(2, "source is subchannel %04X\n", irq); CIO_CRW_EVENT(2, "source is subchannel %04X\n", irq);
if (need_rescan)
/* We need to iterate all subchannels anyway. */
return -EAGAIN;
/* /*
* Since we are always presented with IPI in the CRW, we have to * Since we are always presented with IPI in the CRW, we have to
* use stsch() to find out if the subchannel in question has come * use stsch() to find out if the subchannel in question has come
* or gone. * or gone.
*/ */
css_evaluate_subchannel(irq); ret = css_evaluate_subchannel(irq, 0);
if (ret == -EAGAIN) {
if (css_enqueue_subchannel_slow(irq)) {
css_clear_subchannel_slow_list();
need_rescan = 1;
}
}
return ret;
} }
/* /*
...@@ -412,6 +457,73 @@ s390_root_dev_unregister(struct device *dev) ...@@ -412,6 +457,73 @@ s390_root_dev_unregister(struct device *dev)
device_unregister(dev); device_unregister(dev);
} }
struct slow_subchannel {
struct list_head slow_list;
unsigned long schid;
};
static LIST_HEAD(slow_subchannels_head);
static spinlock_t slow_subchannel_lock = SPIN_LOCK_UNLOCKED;
int
css_enqueue_subchannel_slow(unsigned long schid)
{
struct slow_subchannel *new_slow_sch;
unsigned long flags;
new_slow_sch = kmalloc(sizeof(struct slow_subchannel), GFP_ATOMIC);
if (!new_slow_sch)
return -ENOMEM;
new_slow_sch->schid = schid;
spin_lock_irqsave(&slow_subchannel_lock, flags);
list_add_tail(&new_slow_sch->slow_list, &slow_subchannels_head);
spin_unlock_irqrestore(&slow_subchannel_lock, flags);
return 0;
}
void
css_clear_subchannel_slow_list(void)
{
unsigned long flags;
spin_lock_irqsave(&slow_subchannel_lock, flags);
while (!list_empty(&slow_subchannels_head)) {
struct slow_subchannel *slow_sch =
list_entry(slow_subchannels_head.next,
struct slow_subchannel, slow_list);
list_del_init(slow_subchannels_head.next);
kfree(slow_sch);
}
spin_unlock_irqrestore(&slow_subchannel_lock, flags);
}
void
css_walk_subchannel_slow_list(void (*fn)(unsigned long))
{
unsigned long flags;
spin_lock_irqsave(&slow_subchannel_lock, flags);
while (!list_empty(&slow_subchannels_head)) {
struct slow_subchannel *slow_sch =
list_entry(slow_subchannels_head.next,
struct slow_subchannel, slow_list);
list_del_init(slow_subchannels_head.next);
spin_unlock_irqrestore(&slow_subchannel_lock, flags);
fn(slow_sch->schid);
spin_lock_irqsave(&slow_subchannel_lock, flags);
kfree(slow_sch);
}
spin_unlock_irqrestore(&slow_subchannel_lock, flags);
}
int
css_slow_subchannels_exist(void)
{
return (!list_empty(&slow_subchannels_head));
}
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
EXPORT_SYMBOL(css_bus_type); EXPORT_SYMBOL(css_bus_type);
EXPORT_SYMBOL(s390_root_dev_register); EXPORT_SYMBOL(s390_root_dev_register);
......
...@@ -65,6 +65,7 @@ struct senseid { ...@@ -65,6 +65,7 @@ struct senseid {
struct ccw_device_private { struct ccw_device_private {
int state; /* device state */ int state; /* device state */
atomic_t onoff;
__u16 devno; /* device number */ __u16 devno; /* device number */
__u16 irq; /* subchannel number */ __u16 irq; /* subchannel number */
__u8 imask; /* lpm mask for SNID/SID/SPGID */ __u8 imask; /* lpm mask for SNID/SID/SPGID */
...@@ -127,6 +128,14 @@ int device_is_disconnected(struct subchannel *); ...@@ -127,6 +128,14 @@ int device_is_disconnected(struct subchannel *);
void device_set_disconnected(struct subchannel *); void device_set_disconnected(struct subchannel *);
void device_trigger_reprobe(struct subchannel *); void device_trigger_reprobe(struct subchannel *);
/* Helper function for vary on/off. */ /* Helper functions for vary on/off. */
void device_set_waiting(struct subchannel *); void device_set_waiting(struct subchannel *);
void device_call_nopath_notify(struct subchannel *);
/* Helper functions to build lists for the slow path. */
int css_enqueue_subchannel_slow(unsigned long schid);
void css_walk_subchannel_slow_list(void (*fn)(unsigned long));
void css_clear_subchannel_slow_list(void);
int css_slow_subchannels_exist(void);
extern int need_rescan;
#endif #endif
/* /*
* drivers/s390/cio/device.c * drivers/s390/cio/device.c
* bus driver for ccw devices * bus driver for ccw devices
* $Revision: 1.85 $ * $Revision: 1.103 $
* *
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -240,6 +240,22 @@ online_show (struct device *dev, char *buf) ...@@ -240,6 +240,22 @@ online_show (struct device *dev, char *buf)
return sprintf(buf, cdev->online ? "1\n" : "0\n"); return sprintf(buf, cdev->online ? "1\n" : "0\n");
} }
static void
ccw_device_remove_disconnected(struct ccw_device *cdev)
{
struct subchannel *sch;
/*
* Forced offline in disconnected state means
* 'throw away device'.
*/
sch = to_subchannel(cdev->dev.parent);
device_unregister(&sch->dev);
/* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0;
cio_modify(sch);
put_device(&sch->dev);
}
int int
ccw_device_set_offline(struct ccw_device *cdev) ccw_device_set_offline(struct ccw_device *cdev)
{ {
...@@ -250,20 +266,6 @@ ccw_device_set_offline(struct ccw_device *cdev) ...@@ -250,20 +266,6 @@ ccw_device_set_offline(struct ccw_device *cdev)
if (!cdev->online || !cdev->drv) if (!cdev->online || !cdev->drv)
return -EINVAL; return -EINVAL;
if (cdev->private->state == DEV_STATE_DISCONNECTED) {
struct subchannel *sch;
/*
* Forced offline in disconnected state means
* 'throw away device'.
*/
sch = to_subchannel(cdev->dev.parent);
device_unregister(&sch->dev);
/* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0;
cio_modify(sch);
put_device(&sch->dev);
return 0;
}
if (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)
...@@ -280,7 +282,7 @@ ccw_device_set_offline(struct ccw_device *cdev) ...@@ -280,7 +282,7 @@ ccw_device_set_offline(struct ccw_device *cdev)
ret, cdev->dev.bus_id); ret, cdev->dev.bus_id);
cdev->online = 1; cdev->online = 1;
} }
return ret; return ret;
} }
int int
...@@ -329,15 +331,19 @@ online_store (struct device *dev, const char *buf, size_t count) ...@@ -329,15 +331,19 @@ online_store (struct device *dev, const char *buf, size_t count)
if (!cdev->drv) if (!cdev->drv)
return count; return count;
if (atomic_compare_and_swap(0, 1, &cdev->private->onoff))
return -EAGAIN;
i = simple_strtoul(buf, &tmp, 16); i = simple_strtoul(buf, &tmp, 16);
if (i == 1 && cdev->drv->set_online) if (i == 1 && cdev->drv->set_online)
ccw_device_set_online(cdev); ccw_device_set_online(cdev);
else if (i == 0 && cdev->drv->set_offline) else if (i == 0 && cdev->drv->set_offline) {
ccw_device_set_offline(cdev); if (cdev->private->state == DEV_STATE_DISCONNECTED)
else ccw_device_remove_disconnected(cdev);
return -EINVAL; else
ccw_device_set_offline(cdev);
}
atomic_set(&cdev->private->onoff, 0);
return count; return count;
} }
...@@ -369,12 +375,36 @@ stlck_store(struct device *dev, const char *buf, size_t count) ...@@ -369,12 +375,36 @@ stlck_store(struct device *dev, const char *buf, size_t count)
return count; return count;
} }
static ssize_t
available_show (struct device *dev, char *buf)
{
struct ccw_device *cdev = to_ccwdev(dev);
struct subchannel *sch;
switch (cdev->private->state) {
case DEV_STATE_BOXED:
return sprintf(buf, "boxed\n");
case DEV_STATE_DISCONNECTED:
case DEV_STATE_DISCONNECTED_SENSE_ID:
case DEV_STATE_NOT_OPER:
sch = to_subchannel(dev->parent);
if (!sch->lpm)
return sprintf(buf, "no path\n");
else
return sprintf(buf, "no device\n");
default:
/* All other states considered fine. */
return sprintf(buf, "good\n");
}
}
static DEVICE_ATTR(chpids, 0444, chpids_show, NULL); static DEVICE_ATTR(chpids, 0444, chpids_show, NULL);
static DEVICE_ATTR(pimpampom, 0444, pimpampom_show, NULL); static DEVICE_ATTR(pimpampom, 0444, pimpampom_show, NULL);
static DEVICE_ATTR(devtype, 0444, devtype_show, NULL); static DEVICE_ATTR(devtype, 0444, devtype_show, NULL);
static DEVICE_ATTR(cutype, 0444, cutype_show, NULL); static DEVICE_ATTR(cutype, 0444, cutype_show, NULL);
static DEVICE_ATTR(online, 0644, online_show, online_store); static DEVICE_ATTR(online, 0644, online_show, online_store);
static DEVICE_ATTR(steal_lock, 0200, NULL, stlck_store); static DEVICE_ATTR(steal_lock, 0200, NULL, stlck_store);
static DEVICE_ATTR(availability, 0444, available_show, NULL);
/* A device has been unboxed. Start device recognition. */ /* A device has been unboxed. Start device recognition. */
static void static void
...@@ -419,6 +449,7 @@ static struct attribute * ccwdev_attrs[] = { ...@@ -419,6 +449,7 @@ static struct attribute * ccwdev_attrs[] = {
&dev_attr_devtype.attr, &dev_attr_devtype.attr,
&dev_attr_cutype.attr, &dev_attr_cutype.attr,
&dev_attr_online.attr, &dev_attr_online.attr,
&dev_attr_availability.attr,
NULL, NULL,
}; };
...@@ -468,7 +499,7 @@ ccw_device_register(struct ccw_device *cdev) ...@@ -468,7 +499,7 @@ ccw_device_register(struct ccw_device *cdev)
return ret; return ret;
if ((ret = device_add_files(dev))) if ((ret = device_add_files(dev)))
device_unregister(dev); device_del(dev);
return ret; return ret;
} }
...@@ -521,6 +552,7 @@ io_subchannel_register(void *data) ...@@ -521,6 +552,7 @@ io_subchannel_register(void *data)
if (ret) { if (ret) {
printk (KERN_WARNING "%s: could not register %s\n", printk (KERN_WARNING "%s: could not register %s\n",
__func__, cdev->dev.bus_id); __func__, cdev->dev.bus_id);
put_device(&cdev->dev);
sch->dev.driver_data = 0; sch->dev.driver_data = 0;
kfree (cdev->private); kfree (cdev->private);
kfree (cdev); kfree (cdev);
...@@ -533,10 +565,25 @@ io_subchannel_register(void *data) ...@@ -533,10 +565,25 @@ io_subchannel_register(void *data)
__func__, sch->dev.bus_id); __func__, sch->dev.bus_id);
if (cdev->private->state == DEV_STATE_BOXED) if (cdev->private->state == DEV_STATE_BOXED)
device_create_file(&cdev->dev, &dev_attr_steal_lock); device_create_file(&cdev->dev, &dev_attr_steal_lock);
put_device(&cdev->dev);
out: out:
put_device(&sch->dev); put_device(&sch->dev);
} }
static void
device_call_sch_unregister(void *data)
{
struct ccw_device *cdev = data;
struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent);
device_unregister(&sch->dev);
/* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0;
cio_modify(sch);
put_device(&cdev->dev);
}
/* /*
* subchannel recognition done. Called from the state machine. * subchannel recognition done. Called from the state machine.
*/ */
...@@ -550,11 +597,12 @@ io_subchannel_recog_done(struct ccw_device *cdev) ...@@ -550,11 +597,12 @@ io_subchannel_recog_done(struct ccw_device *cdev)
switch (cdev->private->state) { switch (cdev->private->state) {
case DEV_STATE_NOT_OPER: case DEV_STATE_NOT_OPER:
/* Remove device found not operational. */ /* Remove device found not operational. */
if (!get_device(&cdev->dev))
break;
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
sch->dev.driver_data = 0; INIT_WORK(&cdev->private->kick_work,
put_device(&sch->dev); device_call_sch_unregister, (void *) cdev);
if (cdev->dev.release) queue_work(ccw_device_work, &cdev->private->kick_work);
cdev->dev.release(&cdev->dev);
break; break;
case DEV_STATE_BOXED: case DEV_STATE_BOXED:
/* Device did not respond in time. */ /* Device did not respond in time. */
...@@ -563,6 +611,8 @@ io_subchannel_recog_done(struct ccw_device *cdev) ...@@ -563,6 +611,8 @@ io_subchannel_recog_done(struct ccw_device *cdev)
* We can't register the device in interrupt context so * We can't register the device in interrupt context so
* we schedule a work item. * we schedule a work item.
*/ */
if (!get_device(&cdev->dev))
break;
INIT_WORK(&cdev->private->kick_work, INIT_WORK(&cdev->private->kick_work,
io_subchannel_register, (void *) cdev); io_subchannel_register, (void *) cdev);
queue_work(ccw_device_work, &cdev->private->kick_work); queue_work(ccw_device_work, &cdev->private->kick_work);
...@@ -647,6 +697,7 @@ io_subchannel_probe (struct device *pdev) ...@@ -647,6 +697,7 @@ io_subchannel_probe (struct device *pdev)
return -ENOMEM; return -ENOMEM;
} }
memset(cdev->private, 0, sizeof(struct ccw_device_private)); memset(cdev->private, 0, sizeof(struct ccw_device_private));
atomic_set(&cdev->private->onoff, 0);
cdev->dev = (struct device) { cdev->dev = (struct device) {
.parent = pdev, .parent = pdev,
.release = ccw_device_release, .release = ccw_device_release,
...@@ -657,18 +708,17 @@ io_subchannel_probe (struct device *pdev) ...@@ -657,18 +708,17 @@ io_subchannel_probe (struct device *pdev)
if (!get_device(&sch->dev)) { if (!get_device(&sch->dev)) {
if (cdev->dev.release) if (cdev->dev.release)
cdev->dev.release(&cdev->dev); cdev->dev.release(&cdev->dev);
return 0; return -ENODEV;
} }
rc = io_subchannel_recog(cdev, to_subchannel(pdev)); rc = io_subchannel_recog(cdev, to_subchannel(pdev));
if (rc) { if (rc) {
sch->dev.driver_data = 0; sch->dev.driver_data = 0;
put_device(&sch->dev);
if (cdev->dev.release) if (cdev->dev.release)
cdev->dev.release(&cdev->dev); cdev->dev.release(&cdev->dev);
} }
return 0; return rc;
} }
static int static int
...@@ -680,8 +730,14 @@ io_subchannel_remove (struct device *dev) ...@@ -680,8 +730,14 @@ io_subchannel_remove (struct device *dev)
return 0; return 0;
cdev = dev->driver_data; cdev = dev->driver_data;
/* Set ccw device to not operational and drop reference. */ /* Set ccw device to not operational and drop reference. */
dev_fsm_event(cdev, DEV_EVENT_NOTOPER); cdev->private->state = DEV_STATE_NOT_OPER;
put_device(&cdev->dev); /*
* Careful here. Our ccw device might be yet unregistered when
* de-registering its subchannel (machine check during device
* recognition). Better look if the subchannel has children.
*/
if (!list_empty(&dev->children))
device_unregister(&cdev->dev);
dev->driver_data = NULL; dev->driver_data = NULL;
return 0; return 0;
} }
...@@ -860,10 +916,7 @@ ccw_device_remove (struct device *dev) ...@@ -860,10 +916,7 @@ ccw_device_remove (struct device *dev)
struct ccw_driver *cdrv = cdev->drv; struct ccw_driver *cdrv = cdev->drv;
int ret; int ret;
pr_debug("removing device %s, sch %d, devno %x\n", pr_debug("removing device %s\n", cdev->dev.bus_id);
cdev->dev.bus_id,
cdev->private->irq,
cdev->private->devno);
if (cdrv->remove) if (cdrv->remove)
cdrv->remove(cdev); cdrv->remove(cdev);
if (cdev->online) { if (cdev->online) {
...@@ -879,6 +932,7 @@ ccw_device_remove (struct device *dev) ...@@ -879,6 +932,7 @@ ccw_device_remove (struct device *dev)
pr_debug("ccw_device_offline returned %d, device %s\n", pr_debug("ccw_device_offline returned %d, device %s\n",
ret, cdev->dev.bus_id); ret, cdev->dev.bus_id);
} }
cdev->drv = 0;
return 0; return 0;
} }
......
This diff is collapsed.
...@@ -195,7 +195,7 @@ __ccw_device_sense_id_start(struct ccw_device *cdev) ...@@ -195,7 +195,7 @@ __ccw_device_sense_id_start(struct ccw_device *cdev)
/* Try on every path. */ /* Try on every path. */
ret = -ENODEV; ret = -ENODEV;
while (cdev->private->imask != 0) { while (cdev->private->imask != 0) {
if ((sch->lpm & cdev->private->imask) != 0 && if ((sch->opm & cdev->private->imask) != 0 &&
cdev->private->iretry > 0) { cdev->private->iretry > 0) {
cdev->private->iretry--; cdev->private->iretry--;
ret = cio_start (sch, cdev->private->iccws, ret = cio_start (sch, cdev->private->iccws,
...@@ -246,22 +246,26 @@ ccw_device_check_sense_id(struct ccw_device *cdev) ...@@ -246,22 +246,26 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
/* Check the error cases. */ /* Check the error cases. */
if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
return -ETIME; return -ETIME;
if (irb->esw.esw0.erw.cons && if (irb->esw.esw0.erw.cons && (irb->ecw[0] & SNS0_CMD_REJECT)) {
(irb->ecw[0] & (SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ))) {
/* /*
* if the device doesn't support the SenseID * if the device doesn't support the SenseID
* command further retries wouldn't help ... * command further retries wouldn't help ...
* NB: We don't check here for intervention required like we
* did before, because tape devices with no tape inserted
* may present this status *in conjunction with* the
* sense id information. So, for intervention required,
* we use the "whack it until it talks" strategy...
*/ */
CIO_MSG_EVENT(2, "SenseID : device %s on Subchannel %s " CIO_MSG_EVENT(2, "SenseID : device %04x on Subchannel %04x "
"reports cmd reject or intervention required\n", "reports cmd reject\n",
cdev->dev.bus_id, sch->dev.bus_id); cdev->private->devno, sch->irq);
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
if (irb->esw.esw0.erw.cons) { if (irb->esw.esw0.erw.cons) {
CIO_MSG_EVENT(2, "SenseID : UC on dev %s, " CIO_MSG_EVENT(2, "SenseID : UC on dev %04x, "
"lpum %02X, cnt %02d, sns :" "lpum %02X, cnt %02d, sns :"
" %02X%02X%02X%02X %02X%02X%02X%02X ...\n", " %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
cdev->dev.bus_id, cdev->private->devno,
irb->esw.esw0.sublog.lpum, irb->esw.esw0.sublog.lpum,
irb->esw.esw0.erw.scnt, irb->esw.esw0.erw.scnt,
irb->ecw[0], irb->ecw[1], irb->ecw[0], irb->ecw[1],
...@@ -271,15 +275,18 @@ ccw_device_check_sense_id(struct ccw_device *cdev) ...@@ -271,15 +275,18 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
return -EAGAIN; return -EAGAIN;
} }
if (irb->scsw.cc == 3) { if (irb->scsw.cc == 3) {
CIO_MSG_EVENT(2, "SenseID : path %02X for device %s on " if ((sch->orb.lpm &
"subchannel %s is 'not operational'\n", sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
sch->orb.lpm, cdev->dev.bus_id, sch->dev.bus_id); CIO_MSG_EVENT(2, "SenseID : path %02X for device %04x on"
" subchannel %04x is 'not operational'\n",
sch->orb.lpm, cdev->private->devno,
sch->irq);
return -EACCES; return -EACCES;
} }
/* Hmm, whatever happened, try again. */ /* Hmm, whatever happened, try again. */
CIO_MSG_EVENT(2, "SenseID : start_IO() for device %s on " CIO_MSG_EVENT(2, "SenseID : start_IO() for device %04x on "
"subchannel %s returns status %02X%02X\n", "subchannel %04x returns status %02X%02X\n",
cdev->dev.bus_id, sch->dev.bus_id, cdev->private->devno, sch->irq,
irb->scsw.dstat, irb->scsw.cstat); irb->scsw.dstat, irb->scsw.cstat);
return -EAGAIN; return -EAGAIN;
} }
......
...@@ -154,6 +154,7 @@ ccw_device_call_handler(struct ccw_device *cdev) ...@@ -154,6 +154,7 @@ ccw_device_call_handler(struct ccw_device *cdev)
{ {
struct subchannel *sch; struct subchannel *sch;
unsigned int stctl; unsigned int stctl;
int ending_status;
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
...@@ -166,7 +167,10 @@ ccw_device_call_handler(struct ccw_device *cdev) ...@@ -166,7 +167,10 @@ ccw_device_call_handler(struct ccw_device *cdev)
* - unsolicited interrupts * - unsolicited interrupts
*/ */
stctl = cdev->private->irb.scsw.stctl; stctl = cdev->private->irb.scsw.stctl;
if (sch->schib.scsw.actl != 0 && ending_status = (stctl & SCSW_STCTL_SEC_STATUS) ||
(stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) ||
(stctl == SCSW_STCTL_STATUS_PEND);
if (!ending_status &&
!cdev->private->options.repall && !cdev->private->options.repall &&
!(stctl & SCSW_STCTL_INTER_STATUS) && !(stctl & SCSW_STCTL_INTER_STATUS) &&
!(cdev->private->options.fast && !(cdev->private->options.fast &&
...@@ -469,6 +473,7 @@ ccw_device_stlck(struct ccw_device *cdev) ...@@ -469,6 +473,7 @@ ccw_device_stlck(struct ccw_device *cdev)
cio_disable_subchannel(sch); //FIXME: return code? cio_disable_subchannel(sch); //FIXME: return code?
goto out_unlock; goto out_unlock;
} }
cdev->private->irb.scsw.actl |= SCSW_ACTL_START_PEND;
spin_unlock_irqrestore(&sch->lock, flags); spin_unlock_irqrestore(&sch->lock, flags);
wait_event(cdev->private->wait_q, cdev->private->irb.scsw.actl == 0); wait_event(cdev->private->wait_q, cdev->private->irb.scsw.actl == 0);
spin_lock_irqsave(&sch->lock, flags); spin_lock_irqsave(&sch->lock, flags);
......
...@@ -55,10 +55,10 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev) ...@@ -55,10 +55,10 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
/* ret is 0, -EBUSY, -EACCES or -ENODEV */ /* ret is 0, -EBUSY, -EACCES or -ENODEV */
if (ret != -EACCES) if (ret != -EACCES)
return ret; return ret;
CIO_MSG_EVENT(2, "SNID - Device %s on Subchannel " CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel "
"%s, lpm %02X, became 'not " "%04x, lpm %02X, became 'not "
"operational'\n", "operational'\n",
cdev->dev.bus_id, sch->dev.bus_id, cdev->private->devno, sch->irq,
cdev->private->imask); cdev->private->imask);
} }
...@@ -105,10 +105,10 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev) ...@@ -105,10 +105,10 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
if (irb->esw.esw0.erw.cons) { if (irb->esw.esw0.erw.cons) {
CIO_MSG_EVENT(2, "SNID - device %s, unit check, " CIO_MSG_EVENT(2, "SNID - device %04x, unit check, "
"lpum %02X, cnt %02d, sns : " "lpum %02X, cnt %02d, sns : "
"%02X%02X%02X%02X %02X%02X%02X%02X ...\n", "%02X%02X%02X%02X %02X%02X%02X%02X ...\n",
cdev->dev.bus_id, cdev->private->devno,
irb->esw.esw0.sublog.lpum, irb->esw.esw0.sublog.lpum,
irb->esw.esw0.erw.scnt, irb->esw.esw0.erw.scnt,
irb->ecw[0], irb->ecw[1], irb->ecw[0], irb->ecw[1],
...@@ -118,15 +118,15 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev) ...@@ -118,15 +118,15 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
return -EAGAIN; return -EAGAIN;
} }
if (irb->scsw.cc == 3) { if (irb->scsw.cc == 3) {
CIO_MSG_EVENT(2, "SNID - Device %s on Subchannel " CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel "
"%s, lpm %02X, became 'not operational'\n", "%04x, lpm %02X, became 'not operational'\n",
cdev->dev.bus_id, sch->dev.bus_id, sch->orb.lpm); cdev->private->devno, sch->irq, sch->orb.lpm);
return -EACCES; return -EACCES;
} }
if (cdev->private->pgid.inf.ps.state2 == SNID_STATE2_RESVD_ELSE) { if (cdev->private->pgid.inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
CIO_MSG_EVENT(2, "SNID - Device %s on Subchannel %s " CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel %04x "
"is reserved by someone else\n", "is reserved by someone else\n",
cdev->dev.bus_id, sch->dev.bus_id); cdev->private->devno, sch->irq);
return -EUSERS; return -EUSERS;
} }
return 0; return 0;
...@@ -233,9 +233,9 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func) ...@@ -233,9 +233,9 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
/* PGID command failed on this path. Switch it off. */ /* PGID command failed on this path. Switch it off. */
sch->lpm &= ~cdev->private->imask; sch->lpm &= ~cdev->private->imask;
sch->vpm &= ~cdev->private->imask; sch->vpm &= ~cdev->private->imask;
CIO_MSG_EVENT(2, "SPID - Device %s on Subchannel " CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel "
"%s, lpm %02X, became 'not operational'\n", "%04x, lpm %02X, became 'not operational'\n",
cdev->dev.bus_id, sch->dev.bus_id, cdev->private->imask); cdev->private->devno, sch->irq, cdev->private->imask);
return ret; return ret;
} }
...@@ -257,9 +257,9 @@ __ccw_device_check_pgid(struct ccw_device *cdev) ...@@ -257,9 +257,9 @@ __ccw_device_check_pgid(struct ccw_device *cdev)
if (irb->ecw[0] & SNS0_CMD_REJECT) if (irb->ecw[0] & SNS0_CMD_REJECT)
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* Hmm, whatever happened, try again. */ /* Hmm, whatever happened, try again. */
CIO_MSG_EVENT(2, "SPID - device %s, unit check, cnt %02d, " CIO_MSG_EVENT(2, "SPID - device %04x, unit check, cnt %02d, "
"sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n", "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
cdev->dev.bus_id, irb->esw.esw0.erw.scnt, cdev->private->devno, irb->esw.esw0.erw.scnt,
irb->ecw[0], irb->ecw[1], irb->ecw[0], irb->ecw[1],
irb->ecw[2], irb->ecw[3], irb->ecw[2], irb->ecw[3],
irb->ecw[4], irb->ecw[5], irb->ecw[4], irb->ecw[5],
...@@ -267,9 +267,9 @@ __ccw_device_check_pgid(struct ccw_device *cdev) ...@@ -267,9 +267,9 @@ __ccw_device_check_pgid(struct ccw_device *cdev)
return -EAGAIN; return -EAGAIN;
} }
if (irb->scsw.cc == 3) { if (irb->scsw.cc == 3) {
CIO_MSG_EVENT(2, "SPID - Device %s on Subchannel " CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel "
"%s, lpm %02X, became 'not operational'\n", "%04x, lpm %02X, became 'not operational'\n",
cdev->dev.bus_id, sch->dev.bus_id, cdev->private->devno, sch->irq,
cdev->private->imask); cdev->private->imask);
return -EACCES; return -EACCES;
} }
......
...@@ -62,8 +62,8 @@ ccw_device_path_notoper(struct ccw_device *cdev) ...@@ -62,8 +62,8 @@ ccw_device_path_notoper(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent); sch = to_subchannel(cdev->dev.parent);
stsch (sch->irq, &sch->schib); stsch (sch->irq, &sch->schib);
CIO_MSG_EVENT(0, "%s(%s) - path(s) %02x are " CIO_MSG_EVENT(0, "%s(%04x) - path(s) %02x are "
"not operational \n", __FUNCTION__, sch->dev.bus_id, "not operational \n", __FUNCTION__, sch->irq,
sch->schib.pmcw.pnom); sch->schib.pmcw.pnom);
sch->lpm &= ~sch->schib.pmcw.pnom; sch->lpm &= ~sch->schib.pmcw.pnom;
...@@ -228,8 +228,8 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb) ...@@ -228,8 +228,8 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb)
cdev_irb->scsw.key = irb->scsw.key; cdev_irb->scsw.key = irb->scsw.key;
/* Copy suspend control bit. */ /* Copy suspend control bit. */
cdev_irb->scsw.sctl = irb->scsw.sctl; cdev_irb->scsw.sctl = irb->scsw.sctl;
/* Copy deferred condition code. */ /* Accumulate deferred condition code. */
cdev_irb->scsw.cc = irb->scsw.cc; cdev_irb->scsw.cc |= irb->scsw.cc;
/* Copy ccw format bit. */ /* Copy ccw format bit. */
cdev_irb->scsw.fmt = irb->scsw.fmt; cdev_irb->scsw.fmt = irb->scsw.fmt;
/* Copy prefetch bit. */ /* Copy prefetch bit. */
......
This diff is collapsed.
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/errno.h>
#include <asm/lowcore.h> #include <asm/lowcore.h>
...@@ -19,12 +20,14 @@ ...@@ -19,12 +20,14 @@
#define DBG printk #define DBG printk
// #define DBG(args,...) do {} while (0); // #define DBG(args,...) do {} while (0);
static struct semaphore m_sem;
static struct semaphore s_sem; static struct semaphore s_sem;
extern void css_process_crw(int); extern int css_process_crw(int);
extern void chsc_process_crw(void); extern int chsc_process_crw(void);
extern void chp_process_crw(int, int); extern int chp_process_crw(int, int);
extern void css_reiterate_subchannels(void); extern void css_reiterate_subchannels(void);
extern void css_trigger_slow_path(void);
static void static void
s390_handle_damage(char *msg) s390_handle_damage(char *msg)
...@@ -36,6 +39,21 @@ s390_handle_damage(char *msg) ...@@ -36,6 +39,21 @@ s390_handle_damage(char *msg)
disabled_wait((unsigned long) __builtin_return_address(0)); disabled_wait((unsigned long) __builtin_return_address(0));
} }
static int
s390_mchk_slow_path(void *param)
{
struct semaphore *sem;
sem = (struct semaphore *)param;
/* Set a nice name. */
daemonize("kslowcrw");
repeat:
down_interruptible(sem);
css_trigger_slow_path();
goto repeat;
return 0;
}
/* /*
* Retrieve CRWs and call function to handle event. * Retrieve CRWs and call function to handle event.
* *
...@@ -45,15 +63,15 @@ static int ...@@ -45,15 +63,15 @@ static int
s390_collect_crw_info(void *param) s390_collect_crw_info(void *param)
{ {
struct crw crw; struct crw crw;
int ccode; int ccode, ret, slow;
struct semaphore *sem; struct semaphore *sem;
sem = (struct semaphore *)param; sem = (struct semaphore *)param;
/* Set a nice name. */ /* Set a nice name. */
daemonize("kmcheck"); daemonize("kmcheck");
repeat: repeat:
down_interruptible(sem); down_interruptible(sem);
slow = 0;
while (1) { while (1) {
ccode = stcrw(&crw); ccode = stcrw(&crw);
if (ccode != 0) if (ccode != 0)
...@@ -66,12 +84,15 @@ s390_collect_crw_info(void *param) ...@@ -66,12 +84,15 @@ s390_collect_crw_info(void *param)
if (crw.oflw) { if (crw.oflw) {
pr_debug("%s: crw overflow detected!\n", __FUNCTION__); pr_debug("%s: crw overflow detected!\n", __FUNCTION__);
css_reiterate_subchannels(); css_reiterate_subchannels();
slow = 1;
continue; continue;
} }
switch (crw.rsc) { switch (crw.rsc) {
case CRW_RSC_SCH: case CRW_RSC_SCH:
pr_debug("source is subchannel %04X\n", crw.rsid); pr_debug("source is subchannel %04X\n", crw.rsid);
css_process_crw (crw.rsid); ret = css_process_crw (crw.rsid);
if (ret == -EAGAIN)
slow = 1;
break; break;
case CRW_RSC_MONITOR: case CRW_RSC_MONITOR:
pr_debug("source is monitoring facility\n"); pr_debug("source is monitoring facility\n");
...@@ -80,28 +101,36 @@ s390_collect_crw_info(void *param) ...@@ -80,28 +101,36 @@ s390_collect_crw_info(void *param)
pr_debug("source is channel path %02X\n", crw.rsid); pr_debug("source is channel path %02X\n", crw.rsid);
switch (crw.erc) { switch (crw.erc) {
case CRW_ERC_IPARM: /* Path has come. */ case CRW_ERC_IPARM: /* Path has come. */
chp_process_crw(crw.rsid, 1); ret = chp_process_crw(crw.rsid, 1);
break; break;
case CRW_ERC_PERRI: /* Path has gone. */ case CRW_ERC_PERRI: /* Path has gone. */
chp_process_crw(crw.rsid, 0); case CRW_ERC_PERRN:
ret = chp_process_crw(crw.rsid, 0);
break; break;
default: default:
pr_debug("Don't know how to handle erc=%x\n", pr_debug("Don't know how to handle erc=%x\n",
crw.erc); crw.erc);
ret = 0;
} }
if (ret == -EAGAIN)
slow = 1;
break; break;
case CRW_RSC_CONFIG: case CRW_RSC_CONFIG:
pr_debug("source is configuration-alert facility\n"); pr_debug("source is configuration-alert facility\n");
break; break;
case CRW_RSC_CSS: case CRW_RSC_CSS:
pr_debug("source is channel subsystem\n"); pr_debug("source is channel subsystem\n");
chsc_process_crw(); ret = chsc_process_crw();
if (ret == -EAGAIN)
slow = 1;
break; break;
default: default:
pr_debug("unknown source\n"); pr_debug("unknown source\n");
break; break;
} }
} }
if (slow)
up(&s_sem);
goto repeat; goto repeat;
return 0; return 0;
} }
...@@ -140,7 +169,7 @@ s390_do_machine_check(void) ...@@ -140,7 +169,7 @@ s390_do_machine_check(void)
"check\n"); "check\n");
if (mci->cp) /* channel report word pending */ if (mci->cp) /* channel report word pending */
up(&s_sem); up(&m_sem);
#ifdef CONFIG_MACHCHK_WARNING #ifdef CONFIG_MACHCHK_WARNING
/* /*
...@@ -172,6 +201,7 @@ s390_do_machine_check(void) ...@@ -172,6 +201,7 @@ s390_do_machine_check(void)
static int static int
machine_check_init(void) machine_check_init(void)
{ {
init_MUTEX_LOCKED(&m_sem);
init_MUTEX_LOCKED( &s_sem ); init_MUTEX_LOCKED( &s_sem );
ctl_clear_bit(14, 25); /* disable damage MCH */ ctl_clear_bit(14, 25); /* disable damage MCH */
ctl_set_bit(14, 26); /* enable degradation MCH */ ctl_set_bit(14, 26); /* enable degradation MCH */
...@@ -195,7 +225,8 @@ arch_initcall(machine_check_init); ...@@ -195,7 +225,8 @@ arch_initcall(machine_check_init);
static int __init static int __init
machine_check_crw_init (void) machine_check_crw_init (void)
{ {
kernel_thread(s390_collect_crw_info, &s_sem, CLONE_FS|CLONE_FILES); kernel_thread(s390_collect_crw_info, &m_sem, CLONE_FS|CLONE_FILES);
kernel_thread(s390_mchk_slow_path, &s_sem, CLONE_FS|CLONE_FILES);
ctl_set_bit(14, 28); /* enable channel report MCH */ ctl_set_bit(14, 28); /* enable channel report MCH */
return 0; return 0;
} }
......
...@@ -10,6 +10,7 @@ struct ccwgroup_device { ...@@ -10,6 +10,7 @@ struct ccwgroup_device {
CCWGROUP_OFFLINE, CCWGROUP_OFFLINE,
CCWGROUP_ONLINE, CCWGROUP_ONLINE,
} state; } state;
atomic_t onoff;
unsigned int count; /* number of attached slave devices */ unsigned int count; /* number of attached slave devices */
struct device dev; /* master device */ struct device dev; /* master device */
struct ccw_device *cdev[0]; /* variable number, allocate as needed */ struct ccw_device *cdev[0]; /* variable number, allocate as needed */
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <asm/cio.h> #include <asm/cio.h>
#include <asm/ccwdev.h>
#define QDIO_NAME "qdio " #define QDIO_NAME "qdio "
......
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