/*
* drivers/s390/cio/blacklist.c
* S/390 common I/O routines -- blacklisting of specific devices
- * $Revision: 1.35 $
+ * $Revision: 1.39 $
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
* These can be single devices or ranges of devices
*/
-/* 65536 bits to indicate if a devno is blacklisted or not */
+/* 65536 bits for each set to indicate if a devno is blacklisted or not */
#define __BL_DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \
(8*sizeof(long)))
-static unsigned long bl_dev[__BL_DEV_WORDS];
+static unsigned long bl_dev[__MAX_SSID + 1][__BL_DEV_WORDS];
typedef enum {add, free} range_action;
/*
* (Un-)blacklist the devices from-to
*/
static inline void
-blacklist_range (range_action action, unsigned int from, unsigned int to)
+blacklist_range (range_action action, unsigned int from, unsigned int to,
+ unsigned int ssid)
{
if (!to)
to = from;
- if (from > to || to > __MAX_SUBCHANNEL) {
+ if (from > to || to > __MAX_SUBCHANNEL || ssid > __MAX_SSID) {
printk (KERN_WARNING "Invalid blacklist range "
- "0x%04x to 0x%04x, skipping\n", from, to);
+ "0.%x.%04x to 0.%x.%04x, skipping\n",
+ ssid, from, ssid, to);
return;
}
for (; from <= to; from++) {
if (action == add)
- set_bit (from, bl_dev);
+ set_bit (from, bl_dev[ssid]);
else
- clear_bit (from, bl_dev);
+ clear_bit (from, bl_dev[ssid]);
}
}
* Shamelessly grabbed from dasd_devmap.c.
*/
static inline int
-blacklist_busid(char **str, int *id0, int *id1, int *devno)
+blacklist_busid(char **str, int *id0, int *ssid, int *devno)
{
int val, old_style;
char *sav;
goto confused;
val = simple_strtoul(*str, str, 16);
if (old_style || (*str)[0] != '.') {
- *id0 = *id1 = 0;
+ *id0 = *ssid = 0;
if (val < 0 || val > 0xffff)
goto confused;
*devno = val;
val = simple_strtoul(*str, str, 16);
if (val < 0 || val > 0xff || (*str)++[0] != '.')
goto confused;
- *id1 = val;
+ *ssid = val;
if (!isxdigit((*str)[0])) /* We require at least one hex digit */
goto confused;
val = simple_strtoul(*str, str, 16);
static inline int
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_ssid, to_ssid;
while (*str != 0 && *str != '\n') {
range_action ra = action;
*/
if (strncmp(str,"all,",4) == 0 || strcmp(str,"all") == 0 ||
strncmp(str,"all\n",4) == 0 || strncmp(str,"all ",4) == 0) {
- from = 0;
- to = __MAX_SUBCHANNEL;
+ int j;
+
str += 3;
+ for (j=0; j <= __MAX_SSID; j++)
+ blacklist_range(ra, 0, __MAX_SUBCHANNEL, j);
} else {
int rc;
rc = blacklist_busid(&str, &from_id0,
- &from_id1, &from);
+ &from_ssid, &from);
if (rc)
continue;
to = from;
to_id0 = from_id0;
- to_id1 = from_id1;
+ to_ssid = from_ssid;
if (*str == '-') {
str++;
rc = blacklist_busid(&str, &to_id0,
- &to_id1, &to);
+ &to_ssid, &to);
if (rc)
continue;
}
strsep(&str, ",\n"));
continue;
}
- if ((from_id0 != to_id0) || (from_id1 != to_id1)) {
+ if ((from_id0 != to_id0) ||
+ (from_ssid != to_ssid)) {
printk(KERN_WARNING "invalid cio_ignore range "
"%x.%x.%04x-%x.%x.%04x\n",
- from_id0, from_id1, from,
- to_id0, to_id1, to);
+ from_id0, from_ssid, from,
+ to_id0, to_ssid, to);
continue;
}
+ pr_debug("blacklist_setup: adding range "
+ "from %x.%x.%04x to %x.%x.%04x\n",
+ from_id0, from_ssid, from, to_id0, to_ssid, to);
+ blacklist_range (ra, from, to, to_ssid);
}
- /* 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);
}
return 1;
}
* Used by validate_subchannel()
*/
int
-is_blacklisted (int devno)
+is_blacklisted (int ssid, int devno)
{
- return test_bit (devno, bl_dev);
+ return test_bit (devno, bl_dev[ssid]);
}
#ifdef CONFIG_PROC_FS
/* Iterator struct for all devices. */
struct ccwdev_iter {
int devno;
+ int ssid;
int in_range;
};
{
struct ccwdev_iter *iter;
- if (*offset > __MAX_SUBCHANNEL)
+ if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
return NULL;
iter = kmalloc(sizeof(struct ccwdev_iter), GFP_KERNEL);
if (!iter)
return ERR_PTR(-ENOMEM);
memset(iter, 0, sizeof(struct ccwdev_iter));
- iter->devno = *offset;
+ iter->ssid = *offset / (__MAX_SUBCHANNEL + 1);
+ iter->devno = *offset % (__MAX_SUBCHANNEL + 1);
return iter;
}
{
struct ccwdev_iter *iter;
- if (*offset > __MAX_SUBCHANNEL)
+ if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
return NULL;
iter = (struct ccwdev_iter *)it;
- iter->devno++;
+ if (iter->devno == __MAX_SUBCHANNEL) {
+ iter->devno = 0;
+ iter->ssid++;
+ if (iter->ssid > __MAX_SSID)
+ return NULL;
+ } else
+ iter->devno++;
(*offset)++;
return iter;
}
struct ccwdev_iter *iter;
iter = (struct ccwdev_iter *)it;
- if (!is_blacklisted(iter->devno))
+ if (!is_blacklisted(iter->ssid, iter->devno))
/* Not blacklisted, nothing to output. */
return 0;
if (!iter->in_range) {
/* First device in range. */
if ((iter->devno == __MAX_SUBCHANNEL) ||
- !is_blacklisted(iter->devno + 1))
+ !is_blacklisted(iter->ssid, iter->devno + 1))
/* Singular device. */
- return seq_printf(s, "0.0.%04x\n", iter->devno);
+ return seq_printf(s, "0.%x.%04x\n",
+ iter->ssid, iter->devno);
iter->in_range = 1;
- return seq_printf(s, "0.0.%04x-", iter->devno);
+ return seq_printf(s, "0.%x.%04x-", iter->ssid, iter->devno);
}
if ((iter->devno == __MAX_SUBCHANNEL) ||
- !is_blacklisted(iter->devno + 1)) {
+ !is_blacklisted(iter->ssid, iter->devno + 1)) {
/* Last device in range. */
iter->in_range = 0;
- return seq_printf(s, "0.0.%04x\n", iter->devno);
+ return seq_printf(s, "0.%x.%04x\n", iter->ssid, iter->devno);
}
return 0;
}
#ifndef S390_BLACKLIST_H
#define S390_BLACKLIST_H
-extern int is_blacklisted (int devno);
+extern int is_blacklisted (int ssid, int devno);
#endif
/*
* drivers/s390/cio/chsc.c
* S/390 common I/O routines -- channel subsystem call
- * $Revision: 1.120 $
+ * $Revision: 1.126 $
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
struct {
struct chsc_header request;
- u16 reserved1;
+ u16 reserved1a:10;
+ u16 ssid:2;
+ u16 reserved1b:4;
u16 f_sch; /* first subchannel */
u16 reserved2;
u16 l_sch; /* last subchannel */
.code = 0x0004,
};
+ ssd_area->ssid = sch->schid.ssid;
ssd_area->f_sch = sch->schid.sch_no;
ssd_area->l_sch = sch->schid.sch_no;
*/
if (ssd_area->st > 3) { /* uhm, that looks strange... */
CIO_CRW_EVENT(0, "Strange subchannel type %d"
- " for sch %04x\n", ssd_area->st,
- sch->schid.sch_no);
+ " for sch 0.%x.%04x\n", ssd_area->st,
+ sch->schid.ssid, sch->schid.sch_no);
/*
* There may have been a new subchannel type defined in the
* time since this code was written; since we don't know which
return 0;
} else {
const char *type[4] = {"I/O", "chsc", "message", "ADM"};
- CIO_CRW_EVENT(6, "ssd: sch %04x is %s subchannel\n",
- sch->schid.sch_no, type[ssd_area->st]);
+ CIO_CRW_EVENT(6, "ssd: sch 0.%x.%04x is %s subchannel\n",
+ sch->schid.ssid, sch->schid.sch_no,
+ type[ssd_area->st]);
sch->ssd_info.valid = 1;
sch->ssd_info.type = ssd_area->st;
* that beast may be on we'll have to do a stsch
* on all devices, grr...
*/
- if (stsch(schid, &schib))
+ if (stsch_err(schid, &schib))
/* We're through */
return need_rescan ? -EAGAIN : -ENXIO;
put_device(&sch->dev);
return 0;
}
- if (stsch(schid, &schib))
+ if (stsch_err(schid, &schib))
/* We're through */
return -ENXIO;
/* Put it on the slow path. */
return (sei_page ? 0 : -ENOMEM);
}
+int __init
+chsc_enable_facility(int operation_code)
+{
+ int ret;
+ struct {
+ struct chsc_header request;
+ u8 reserved1:4;
+ u8 format:4;
+ u8 reserved2;
+ u16 operation_code;
+ u32 reserved3;
+ u32 reserved4;
+ u32 operation_data_area[252];
+ struct chsc_header response;
+ u32 reserved5:4;
+ u32 format2:4;
+ u32 reserved6:24;
+ } *sda_area;
+
+ sda_area = (void *)get_zeroed_page(GFP_KERNEL|GFP_DMA);
+ if (!sda_area)
+ return -ENOMEM;
+ sda_area->request = (struct chsc_header) {
+ .length = 0x0400,
+ .code = 0x0031,
+ };
+ sda_area->operation_code = operation_code;
+
+ ret = chsc(sda_area);
+ if (ret > 0) {
+ ret = (ret == 3) ? -ENODEV : -EBUSY;
+ goto out;
+ }
+ switch (sda_area->response.code) {
+ case 0x0003: /* invalid request block */
+ case 0x0007:
+ ret = -EINVAL;
+ break;
+ case 0x0004: /* command not provided */
+ case 0x0101: /* facility not provided */
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ out:
+ free_page((unsigned long)sda_area);
+ return ret;
+}
+
subsys_initcall(chsc_alloc_sei_area);
struct css_general_char css_general_characteristics;
#define CHSC_SEI_ACC_LINKADDR 2
#define CHSC_SEI_ACC_FULLLINKADDR 3
+#define CHSC_SDA_OC_MSS 0x2
+
struct chsc_header {
u16 length;
u16 code;
extern void *chsc_get_chp_desc(struct subchannel*, int);
+extern int chsc_enable_facility(int);
+
#define to_channelpath(dev) container_of(dev, struct channel_path, dev)
#endif
/*
* drivers/s390/cio/cio.c
* S/390 common I/O routines -- low level i/o calls
- * $Revision: 1.135 $
+ * $Revision: 1.138 $
*
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
stsch (sch->schid, &sch->schib);
CIO_MSG_EVENT(0, "cio_start: 'not oper' status for "
- "subchannel %04x!\n", sch->schid.sch_no);
+ "subchannel 0.%x.%04x!\n", sch->schid.ssid,
+ sch->schid.sch_no);
sprintf(dbf_text, "no%s", sch->dev.bus_id);
CIO_TRACE_EVENT(0, dbf_text);
CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib));
spin_lock_init(&sch->lock);
/* Set a name for the subchannel */
- snprintf (sch->dev.bus_id, BUS_ID_SIZE, "0.0.%04x", schid.sch_no);
+ snprintf (sch->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", schid.ssid,
+ schid.sch_no);
/*
* The first subchannel that is not-operational (ccode==3)
* indicates that there aren't any more devices available.
+ * If stsch gets an exception, it means the current subchannel set
+ * is not valid.
*/
- ccode = stsch (schid, &sch->schib);
+ ccode = stsch_err (schid, &sch->schib);
if (ccode)
- return -ENXIO;
+ return (ccode == 3) ? -ENXIO : ccode;
sch->schid = schid;
/* Copy subchannel type from path management control word. */
*/
if (sch->st != 0) {
CIO_DEBUG(KERN_INFO, 0,
- "Subchannel %04X reports "
+ "Subchannel 0.%x.%04x reports "
"non-I/O subchannel type %04X\n",
- sch->schid.sch_no, sch->st);
+ sch->schid.ssid, sch->schid.sch_no, sch->st);
/* We stop here for non-io subchannels. */
return sch->st;
}
return -ENODEV;
/* Devno is valid. */
- if (is_blacklisted (sch->schib.pmcw.dev)) {
+ if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) {
/*
* This device must not be known to Linux. So we simply
* say that there is no device and return ENODEV.
*/
CIO_MSG_EVENT(0, "Blacklisted device detected "
- "at devno %04X\n", sch->schib.pmcw.dev);
+ "at devno %04X, subchannel set %x\n",
+ sch->schib.pmcw.dev, sch->schid.ssid);
return -ENODEV;
}
sch->opm = 0xff;
- chsc_validate_chpids(sch);
+ if (!cio_is_console(sch->schid))
+ chsc_validate_chpids(sch);
sch->lpm = sch->schib.pmcw.pim &
sch->schib.pmcw.pam &
sch->schib.pmcw.pom &
sch->opm;
CIO_DEBUG(KERN_INFO, 0,
- "Detected device %04X on subchannel %04X"
+ "Detected device %04x on subchannel 0.%x.%04X"
" - PIM = %02X, PAM = %02X, POM = %02X\n",
- sch->schib.pmcw.dev, sch->schid.sch_no, sch->schib.pmcw.pim,
+ sch->schib.pmcw.dev, sch->schid.ssid,
+ sch->schid.sch_no, sch->schib.pmcw.pim,
sch->schib.pmcw.pam, sch->schib.pmcw.pom);
/*
static int
cio_test_for_console(struct subchannel_id schid, void *data)
{
- if (stsch(schid, &console_subchannel.schib) != 0)
+ if (stsch_err(schid, &console_subchannel.schib) != 0)
return -ENXIO;
if (console_subchannel.schib.pmcw.dnv &&
console_subchannel.schib.pmcw.dev ==
{
struct schib schib;
- if (stsch(schid, &schib))
+ if (stsch_err(schid, &schib))
return -ENXIO;
if (!schib.pmcw.ena)
return 0;
/*
* drivers/s390/cio/css.c
* driver for channel subsystem
- * $Revision: 1.85 $
+ * $Revision: 1.93 $
*
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation
int need_rescan = 0;
int css_init_done = 0;
+static int max_ssid = 0;
struct channel_subsystem *css[__MAX_CSSID + 1];
init_subchannel_id(&schid);
ret = -ENODEV;
do {
- ret = fn(schid, data);
- if (ret)
- break;
- } while (schid.sch_no++ < __MAX_SUBCHANNEL);
+ do {
+ ret = fn(schid, data);
+ if (ret)
+ break;
+ } while (schid.sch_no++ < __MAX_SUBCHANNEL);
+ schid.sch_no = 0;
+ } while (schid.ssid++ < max_ssid);
return ret;
}
return -EAGAIN; /* Will be done on the slow path. */
}
event = css_get_subchannel_status(sch, schid);
- CIO_MSG_EVENT(4, "Evaluating schid %04x, event %d, %s, %s path.\n",
- schid.sch_no, event,
+ CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n",
+ schid.ssid, schid.sch_no, event,
sch?(disc?"disconnected":"normal"):"unknown",
slow?"slow":"fast");
switch (event) {
* Called from the machine check handler for subchannel report words.
*/
int
-css_process_crw(int irq)
+css_process_crw(int rsid1, int rsid2)
{
int ret;
struct subchannel_id mchk_schid;
- CIO_CRW_EVENT(2, "source is subchannel %04X\n", irq);
+ CIO_CRW_EVENT(2, "source is subchannel %04X, subsystem id %x\n",
+ rsid1, rsid2);
if (need_rescan)
/* We need to iterate all subchannels anyway. */
return -EAGAIN;
init_subchannel_id(&mchk_schid);
- mchk_schid.sch_no = irq;
+ mchk_schid.sch_no = rsid1;
+ if (rsid2 != 0)
+ mchk_schid.ssid = (rsid2 >> 8) & 3;
+
/*
* 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
if ((ret = bus_register(&css_bus_type)))
goto out;
+ /* Try to enable MSS. */
+ ret = chsc_enable_facility(CHSC_SDA_OC_MSS);
+ switch (ret) {
+ case 0: /* Success. */
+ max_ssid = __MAX_SSID;
+ break;
+ case -ENOMEM:
+ goto out_bus;
+ default:
+ max_ssid = 0;
+ }
/* Setup css structure. */
for (i = 0; i <= __MAX_CSSID; i++) {
css[i] = kmalloc(sizeof(struct channel_subsystem), GFP_KERNEL);
if (!css[i]) {
ret = -ENOMEM;
- goto out_bus;
+ goto out_unregister;
}
setup_css(i);
ret = device_register(&css[i]->device);
return 0;
out_free:
kfree(css[i]);
-out_bus:
+out_unregister:
while (i > 0) {
i--;
device_unregister(&css[i]->device);
}
+out_bus:
bus_unregister(&css_bus_type);
out:
return ret;
unsigned long registered;
__u16 devno; /* device number */
__u16 sch_no; /* subchannel number */
+ __u8 ssid; /* subchannel set id */
__u8 imask; /* lpm mask for SNID/SID/SPGID */
int iretry; /* retry counter SNID/SID/SPGID */
struct {
extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
#define __MAX_SUBCHANNEL 65535
+#define __MAX_SSID 3
#define __MAX_CHPID 255
#define __MAX_CSSID 0
}
struct match_data {
- unsigned int devno;
+ unsigned int devno;
+ unsigned int ssid;
struct ccw_device * sibling;
};
cdev = to_ccwdev(dev);
if ((cdev->private->state == DEV_STATE_DISCONNECTED) &&
(cdev->private->devno == d->devno) &&
+ (cdev->private->ssid == d->ssid) &&
(cdev != d->sibling)) {
cdev->private->state = DEV_STATE_NOT_OPER;
return 1;
}
static struct ccw_device *
-get_disc_ccwdev_by_devno(unsigned int devno, struct ccw_device *sibling)
+get_disc_ccwdev_by_devno(unsigned int devno, unsigned int ssid,
+ struct ccw_device *sibling)
{
struct device *dev;
struct match_data data = {
- .devno = devno,
+ .devno = devno,
+ .ssid = ssid,
.sibling = sibling,
};
need_rename = 1;
other_cdev = get_disc_ccwdev_by_devno(sch->schib.pmcw.dev,
- cdev);
+ sch->schid.ssid, cdev);
if (other_cdev) {
struct subchannel *other_sch;
if (test_and_clear_bit(1, &cdev->private->registered))
device_del(&cdev->dev);
if (need_rename)
- snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.0.%04x",
- sch->schib.pmcw.dev);
+ snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x",
+ sch->schid.ssid, sch->schib.pmcw.dev);
PREPARE_WORK(&cdev->private->kick_work,
ccw_device_add_changed, (void *)cdev);
queue_work(ccw_device_work, &cdev->private->kick_work);
sch->dev.driver_data = cdev;
sch->driver = &io_subchannel_driver;
cdev->ccwlock = &sch->lock;
+
/* Init private data. */
priv = cdev->private;
priv->devno = sch->schib.pmcw.dev;
+ priv->ssid = sch->schid.ssid;
priv->sch_no = sch->schid.sch_no;
priv->state = DEV_STATE_NOT_OPER;
INIT_LIST_HEAD(&priv->cmb_list);
init_timer(&priv->timer);
/* Set an initial name for the device. */
- snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.0.%04x",
- sch->schib.pmcw.dev);
+ snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x",
+ sch->schid.ssid, sch->schib.pmcw.dev);
/* Increase counter of devices currently in recognition. */
atomic_inc(&ccw_device_init_count);
switch (state) {
case DEV_STATE_NOT_OPER:
CIO_DEBUG(KERN_WARNING, 2,
- "SenseID : unknown device %04x on subchannel %04x\n",
- cdev->private->devno, sch->schid.sch_no);
+ "SenseID : unknown device %04x on subchannel "
+ "0.%x.%04x\n", cdev->private->devno,
+ sch->schid.ssid, sch->schid.sch_no);
break;
case DEV_STATE_OFFLINE:
if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) {
return;
}
/* Issue device info message. */
- CIO_DEBUG(KERN_INFO, 2, "SenseID : device %04x reports: "
+ CIO_DEBUG(KERN_INFO, 2, "SenseID : device 0.%x.%04x reports: "
"CU Type/Mod = %04X/%02X, Dev Type/Mod = "
- "%04X/%02X\n", cdev->private->devno,
+ "%04X/%02X\n",
+ cdev->private->ssid, cdev->private->devno,
cdev->id.cu_type, cdev->id.cu_model,
cdev->id.dev_type, cdev->id.dev_model);
break;
case DEV_STATE_BOXED:
CIO_DEBUG(KERN_WARNING, 2,
- "SenseID : boxed device %04x on subchannel %04x\n",
- cdev->private->devno, sch->schid.sch_no);
+ "SenseID : boxed device %04x on subchannel "
+ "0.%x.%04x\n", cdev->private->devno,
+ sch->schid.ssid, sch->schid.sch_no);
break;
}
cdev->private->state = state;
* sense id information. So, for intervention required,
* we use the "whack it until it talks" strategy...
*/
- CIO_MSG_EVENT(2, "SenseID : device %04x on Subchannel %04x "
- "reports cmd reject\n",
- cdev->private->devno, sch->schid.sch_no);
+ CIO_MSG_EVENT(2, "SenseID : device %04x on Subchannel "
+ "0.%x.%04x reports cmd reject\n",
+ cdev->private->devno, sch->schid.ssid,
+ sch->schid.sch_no);
return -EOPNOTSUPP;
}
if (irb->esw.esw0.erw.cons) {
- CIO_MSG_EVENT(2, "SenseID : UC on dev %04x, "
+ CIO_MSG_EVENT(2, "SenseID : UC on dev 0.%x.%04x, "
"lpum %02X, cnt %02d, sns :"
" %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
- cdev->private->devno,
+ cdev->private->ssid, cdev->private->devno,
irb->esw.esw0.sublog.lpum,
irb->esw.esw0.erw.scnt,
irb->ecw[0], irb->ecw[1],
if (irb->scsw.cc == 3) {
if ((sch->orb.lpm &
sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
- CIO_MSG_EVENT(2, "SenseID : path %02X for device %04x on"
- " subchannel %04x is 'not operational'\n",
- sch->orb.lpm, cdev->private->devno,
+ CIO_MSG_EVENT(2, "SenseID : path %02X for device %04x "
+ "on subchannel 0.%x.%04x is "
+ "'not operational'\n", sch->orb.lpm,
+ cdev->private->devno, sch->schid.ssid,
sch->schid.sch_no);
return -EACCES;
}
/* Hmm, whatever happened, try again. */
CIO_MSG_EVENT(2, "SenseID : start_IO() for device %04x on "
- "subchannel %04x returns status %02X%02X\n",
- cdev->private->devno, sch->schid.sch_no,
+ "subchannel 0.%x.%04x returns status %02X%02X\n",
+ cdev->private->devno, sch->schid.ssid, sch->schid.sch_no,
irb->scsw.dstat, irb->scsw.cstat);
return -EAGAIN;
}
if (ret != -EACCES)
return ret;
CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel "
- "%04x, lpm %02X, became 'not "
+ "0.%x.%04x, lpm %02X, became 'not "
"operational'\n",
- cdev->private->devno, sch->schid.sch_no,
- cdev->private->imask);
+ cdev->private->devno, sch->schid.ssid,
+ sch->schid.sch_no, cdev->private->imask);
}
cdev->private->imask >>= 1;
return -EOPNOTSUPP;
}
if (irb->esw.esw0.erw.cons) {
- CIO_MSG_EVENT(2, "SNID - device %04x, unit check, "
+ CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, "
"lpum %02X, cnt %02d, sns : "
"%02X%02X%02X%02X %02X%02X%02X%02X ...\n",
- cdev->private->devno,
+ cdev->private->ssid, cdev->private->devno,
irb->esw.esw0.sublog.lpum,
irb->esw.esw0.erw.scnt,
irb->ecw[0], irb->ecw[1],
return -EAGAIN;
}
if (irb->scsw.cc == 3) {
- CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel "
- "%04x, lpm %02X, became 'not operational'\n",
- cdev->private->devno, sch->schid.sch_no,
- sch->orb.lpm);
+ CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x,"
+ " lpm %02X, became 'not operational'\n",
+ cdev->private->devno, sch->schid.ssid,
+ sch->schid.sch_no, sch->orb.lpm);
return -EACCES;
}
if (cdev->private->pgid.inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
- CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel %04x "
+ CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x "
"is reserved by someone else\n",
- cdev->private->devno, sch->schid.sch_no);
+ cdev->private->devno, sch->schid.ssid,
+ sch->schid.sch_no);
return -EUSERS;
}
return 0;
sch->lpm &= ~cdev->private->imask;
sch->vpm &= ~cdev->private->imask;
CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel "
- "%04x, lpm %02X, became 'not operational'\n",
- cdev->private->devno, sch->schid.sch_no, cdev->private->imask);
+ "0.%x.%04x, lpm %02X, became 'not operational'\n",
+ cdev->private->devno, sch->schid.ssid,
+ sch->schid.sch_no, cdev->private->imask);
return ret;
}
if (irb->ecw[0] & SNS0_CMD_REJECT)
return -EOPNOTSUPP;
/* Hmm, whatever happened, try again. */
- CIO_MSG_EVENT(2, "SPID - device %04x, unit check, cnt %02d, "
+ CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, "
+ "cnt %02d, "
"sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
+ cdev->private->ssid,
cdev->private->devno, irb->esw.esw0.erw.scnt,
irb->ecw[0], irb->ecw[1],
irb->ecw[2], irb->ecw[3],
return -EAGAIN;
}
if (irb->scsw.cc == 3) {
- CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel "
- "%04x, lpm %02X, became 'not operational'\n",
- cdev->private->devno, sch->schid.sch_no,
- cdev->private->imask);
+ CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel 0.%x.%04x,"
+ " lpm %02X, became 'not operational'\n",
+ cdev->private->devno, sch->schid.ssid,
+ sch->schid.sch_no, cdev->private->imask);
return -EACCES;
}
return 0;
CIO_MSG_EVENT(0, "Channel-Check or Interface-Control-Check "
"received"
- " ... device %04X on subchannel %04X, dev_stat "
+ " ... device %04x on subchannel 0.%x.%04x, dev_stat "
": %02X sch_stat : %02X\n",
- cdev->private->devno, cdev->private->sch_no,
+ cdev->private->devno, cdev->private->ssid,
+ cdev->private->sch_no,
irb->scsw.dstat, irb->scsw.cstat);
if (irb->scsw.cc != 3) {
sch = to_subchannel(cdev->dev.parent);
stsch (sch->schid, &sch->schib);
- CIO_MSG_EVENT(0, "%s(%04x) - path(s) %02x are "
- "not operational \n", __FUNCTION__, sch->schid.sch_no,
+ CIO_MSG_EVENT(0, "%s(0.%x.%04x) - path(s) %02x are "
+ "not operational \n", __FUNCTION__,
+ sch->schid.ssid, sch->schid.sch_no,
sch->schib.pmcw.pnom);
sch->lpm &= ~sch->schib.pmcw.pnom;
return ccode;
}
+static inline int stsch_err(struct subchannel_id schid,
+ volatile struct schib *addr)
+{
+ int ccode;
+
+ __asm__ __volatile__(
+ " lhi %0,%3\n"
+ " lr 1,%1\n"
+ " stsch 0(%2)\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1:\n"
+#ifdef CONFIG_ARCH_S390X
+ ".section __ex_table,\"a\"\n"
+ " .align 8\n"
+ " .quad 0b,1b\n"
+ ".previous"
+#else
+ ".section __ex_table,\"a\"\n"
+ " .align 4\n"
+ " .long 0b,1b\n"
+ ".previous"
+#endif
+ : "=&d" (ccode)
+ : "d" (schid), "a" (addr), "K" (-EIO), "m" (*addr)
+ : "cc", "1" );
+ return ccode;
+}
+
static inline int msch(struct subchannel_id schid,
volatile struct schib *addr)
{
#include "ioasm.h"
#include "chsc.h"
-#define VERSION_QDIO_C "$Revision: 1.113 $"
+#define VERSION_QDIO_C "$Revision: 1.114 $"
/****************** MODULE PARAMETER VARIABLES ********************/
MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
switch (irq_ptr->state) {
case QDIO_IRQ_STATE_INACTIVE:
- QDIO_PRINT_ERR("establish queues on irq %04x: timed out\n",
- irq_ptr->schid.sch_no);
+ QDIO_PRINT_ERR("establish queues on irq 0.%x.%04x: timed out\n",
+ irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
QDIO_DBF_TEXT2(1,setup,"eq:timeo");
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
break;
case QDIO_IRQ_STATE_CLEANUP:
- QDIO_PRINT_INFO("Did not get interrupt on cleanup, irq=0x%x.\n",
- irq_ptr->schid.sch_no);
+ QDIO_PRINT_INFO("Did not get interrupt on cleanup, "
+ "irq=0.%x.%x.\n",
+ irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
break;
case QDIO_IRQ_STATE_ESTABLISHED:
case QDIO_IRQ_STATE_ACTIVE:
/* I/O has been terminated by common I/O layer. */
- QDIO_PRINT_INFO("Queues on irq %04x killed by cio.\n",
- irq_ptr->schid.sch_no);
+ QDIO_PRINT_INFO("Queues on irq 0.%x.%04x killed by cio.\n",
+ irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
QDIO_DBF_TEXT2(1, trace, "cio:term");
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
if (get_device(&cdev->dev)) {
unsigned char qdioac;
struct {
struct chsc_header request;
- u16 reserved1;
+ u16 reserved1:10;
+ u16 ssid:2;
+ u16 fmt:4;
u16 first_sch;
u16 reserved2;
u16 last_sch;
};
ssqd_area->first_sch = irq_ptr->schid.sch_no;
ssqd_area->last_sch = irq_ptr->schid.sch_no;
+ ssqd_area->ssid = irq_ptr->schid.ssid;
result = chsc(ssqd_area);
if (result) {
QDIO_PRINT_WARN("CHSC returned cc %i. Using all " \
- "SIGAs for sch x%x.\n",
- result, irq_ptr->schid.sch_no);
+ "SIGAs for sch 0.%x.%x.\n", result,
+ irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY ||
CHSC_FLAG_SIGA_OUTPUT_NECESSARY ||
CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */
if (ssqd_area->response.code != QDIO_CHSC_RESPONSE_CODE_OK) {
QDIO_PRINT_WARN("response upon checking SIGA needs " \
- "is 0x%x. Using all SIGAs for sch x%x.\n",
- ssqd_area->response.code, irq_ptr->schid.sch_no);
+ "is 0x%x. Using all SIGAs for sch 0.%x.%x.\n",
+ ssqd_area->response.code,
+ irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY ||
CHSC_FLAG_SIGA_OUTPUT_NECESSARY ||
CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */
if (!(ssqd_area->flags & CHSC_FLAG_QDIO_CAPABILITY) ||
!(ssqd_area->flags & CHSC_FLAG_VALIDITY) ||
(ssqd_area->sch != irq_ptr->schid.sch_no)) {
- QDIO_PRINT_WARN("huh? problems checking out sch x%x... " \
- "using all SIGAs.\n",irq_ptr->schid.sch_no);
+ QDIO_PRINT_WARN("huh? problems checking out sch 0.%x.%x... " \
+ "using all SIGAs.\n",
+ irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY |
CHSC_FLAG_SIGA_OUTPUT_NECESSARY |
CHSC_FLAG_SIGA_SYNC_NECESSARY; /* worst case */
scssc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!scssc_area) {
QDIO_PRINT_WARN("No memory for setting indicators on " \
- "subchannel x%x.\n", irq_ptr->schid.sch_no);
+ "subchannel 0.%x.%x.\n",
+ irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
return -ENOMEM;
}
scssc_area->request = (struct chsc_header) {
result = chsc(scssc_area);
if (result) {
- QDIO_PRINT_WARN("could not set indicators on irq x%x, " \
- "cc=%i.\n",irq_ptr->schid.sch_no,result);
+ QDIO_PRINT_WARN("could not set indicators on irq 0.%x.%x, " \
+ "cc=%i.\n",
+ irq_ptr->schid.ssid, irq_ptr->schid.sch_no,result);
result = -EIO;
goto out;
}
scsscf_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!scsscf_area) {
QDIO_PRINT_WARN("No memory for setting delay target on " \
- "subchannel x%x.\n", irq_ptr->schid.sch_no);
+ "subchannel 0.%x.%x.\n",
+ irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
return -ENOMEM;
}
scsscf_area->request = (struct chsc_header) {
result=chsc(scsscf_area);
if (result) {
- QDIO_PRINT_WARN("could not set delay target on irq x%x, " \
- "cc=%i. Continuing.\n",irq_ptr->schid.sch_no,
+ QDIO_PRINT_WARN("could not set delay target on irq 0.%x.%x, " \
+ "cc=%i. Continuing.\n",
+ irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
result);
result = -EIO;
goto out;
QDIO_DBF_HEX2(0,trace,&dstat,sizeof(int));
QDIO_DBF_HEX2(0,trace,&cstat,sizeof(int));
QDIO_PRINT_ERR("received check condition on establish " \
- "queues on irq 0x%x (cs=x%x, ds=x%x).\n",
- irq_ptr->schid.sch_no,cstat,dstat);
+ "queues on irq 0.%x.%x (cs=x%x, ds=x%x).\n",
+ irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
+ cstat,dstat);
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ERR);
}
QDIO_DBF_TEXT2(1,setup,"eq:no de");
QDIO_DBF_HEX2(0,setup,&dstat, sizeof(dstat));
QDIO_DBF_HEX2(0,setup,&cstat, sizeof(cstat));
- QDIO_PRINT_ERR("establish queues on irq %04x: didn't get "
+ QDIO_PRINT_ERR("establish queues on irq 0.%x.%04x: didn't get "
"device end: dstat=%02x, cstat=%02x\n",
- irq_ptr->schid.sch_no, dstat, cstat);
+ irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
+ dstat, cstat);
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
return 1;
}
QDIO_DBF_TEXT2(1,setup,"eq:badio");
QDIO_DBF_HEX2(0,setup,&dstat, sizeof(dstat));
QDIO_DBF_HEX2(0,setup,&cstat, sizeof(cstat));
- QDIO_PRINT_ERR("establish queues on irq %04x: got "
+ QDIO_PRINT_ERR("establish queues on irq 0.%x.%04x: got "
"the following devstat: dstat=%02x, "
- "cstat=%02x\n",
+ "cstat=%02x\n", irq_ptr->schid.ssid,
irq_ptr->schid.sch_no, dstat, cstat);
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
return 1;
QDIO_DBF_HEX1(0,setup,&irq_ptr->dev_st_chg_ind,sizeof(void*));
if (!irq_ptr->dev_st_chg_ind) {
QDIO_PRINT_WARN("no indicator location available " \
- "for irq 0x%x\n",irq_ptr->schid.sch_no);
+ "for irq 0.%x.%x\n",
+ irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
qdio_release_irq_memory(irq_ptr);
return -ENOBUFS;
}
sprintf(dbf_text,"eq:io%4x",result);
QDIO_DBF_TEXT2(1,setup,dbf_text);
}
- QDIO_PRINT_WARN("establish queues on irq %04x: do_IO " \
- "returned %i, next try returned %i\n",
- irq_ptr->schid.sch_no,result,result2);
+ QDIO_PRINT_WARN("establish queues on irq 0.%x.%04x: do_IO " \
+ "returned %i, next try returned %i\n",
+ irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
+ result, result2);
result=result2;
if (result)
ccw_device_set_timeout(cdev, 0);
sprintf(dbf_text,"aq:io%4x",result);
QDIO_DBF_TEXT2(1,setup,dbf_text);
}
- QDIO_PRINT_WARN("activate queues on irq %04x: do_IO " \
- "returned %i, next try returned %i\n",
- irq_ptr->schid.sch_no,result,result2);
+ QDIO_PRINT_WARN("activate queues on irq 0.%x.%04x: do_IO " \
+ "returned %i, next try returned %i\n",
+ irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
+ result, result2);
result=result2;
}
#define S390_SCHID_H
struct subchannel_id {
- __u32 reserved:15;
+ __u32 reserved:13;
+ __u32 ssid:2;
__u32 one:1;
__u32 sch_no:16;
} __attribute__ ((packed,aligned(4)));
static struct semaphore m_sem;
-extern int css_process_crw(int);
+extern int css_process_crw(int, int);
extern int chsc_process_crw(void);
extern int chp_process_crw(int, int);
extern void css_reiterate_subchannels(void);
static int
s390_collect_crw_info(void *param)
{
- struct crw crw;
+ struct crw crw[2];
int ccode, ret, slow;
struct semaphore *sem;
+ unsigned int chain;
sem = (struct semaphore *)param;
/* Set a nice name. */
repeat:
down_interruptible(sem);
slow = 0;
+ chain = 0;
while (1) {
- ccode = stcrw(&crw);
+ if (unlikely(chain > 1)) {
+ struct crw tmp_crw;
+
+ printk(KERN_WARNING"%s: Code does not support more "
+ "than two chained crws; please report to "
+ "linux390@de.ibm.com!\n", __FUNCTION__);
+ ccode = stcrw(&tmp_crw);
+ printk(KERN_WARNING"%s: crw reports slct=%d, oflw=%d, "
+ "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
+ __FUNCTION__, tmp_crw.slct, tmp_crw.oflw,
+ tmp_crw.chn, tmp_crw.rsc, tmp_crw.anc,
+ tmp_crw.erc, tmp_crw.rsid);
+ printk(KERN_WARNING"%s: This was crw number %x in the "
+ "chain\n", __FUNCTION__, chain);
+ if (ccode != 0)
+ break;
+ chain = tmp_crw.chn ? chain + 1 : 0;
+ continue;
+ }
+ ccode = stcrw(&crw[chain]);
if (ccode != 0)
break;
DBG(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, "
"chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
- crw.slct, crw.oflw, crw.chn, crw.rsc, crw.anc,
- crw.erc, crw.rsid);
+ crw[chain].slct, crw[chain].oflw, crw[chain].chn,
+ crw[chain].rsc, crw[chain].anc, crw[chain].erc,
+ crw[chain].rsid);
/* Check for overflows. */
- if (crw.oflw) {
+ if (crw[chain].oflw) {
pr_debug("%s: crw overflow detected!\n", __FUNCTION__);
css_reiterate_subchannels();
+ chain = 0;
slow = 1;
continue;
}
- switch (crw.rsc) {
+ switch (crw[chain].rsc) {
case CRW_RSC_SCH:
- pr_debug("source is subchannel %04X\n", crw.rsid);
- ret = css_process_crw (crw.rsid);
+ if (crw[0].chn && !chain)
+ break;
+ pr_debug("source is subchannel %04X\n", crw[0].rsid);
+ ret = css_process_crw (crw[0].rsid,
+ chain ? crw[1].rsid : 0);
if (ret == -EAGAIN)
slow = 1;
break;
pr_debug("source is monitoring facility\n");
break;
case CRW_RSC_CPATH:
- pr_debug("source is channel path %02X\n", crw.rsid);
- switch (crw.erc) {
+ pr_debug("source is channel path %02X\n", crw[0].rsid);
+ switch (crw[0].erc) {
case CRW_ERC_IPARM: /* Path has come. */
- ret = chp_process_crw(crw.rsid, 1);
+ ret = chp_process_crw(crw[0].rsid, 1);
break;
case CRW_ERC_PERRI: /* Path has gone. */
case CRW_ERC_PERRN:
- ret = chp_process_crw(crw.rsid, 0);
+ ret = chp_process_crw(crw[0].rsid, 0);
break;
default:
pr_debug("Don't know how to handle erc=%x\n",
- crw.erc);
+ crw[0].erc);
ret = 0;
}
if (ret == -EAGAIN)
pr_debug("unknown source\n");
break;
}
+ /* chain is always 0 or 1 here. */
+ chain = crw[chain].chn ? chain + 1 : 0;
}
if (slow)
queue_work(slow_path_wq, &slow_path_work);