[PATCH] s390: multiple subchannel sets support
Add support for multiple subchannel sets. Works with arbitrary devices in subchannel set 1 and is transparent to device drivers. Although currently only two subchannel sets are available, this will work with the architectured maximum number of subchannel sets as well. Signed-off-by: Cornelia Huck <cohuck@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
678a395b35
commit
fb6958a594
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* 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.35 $
|
* $Revision: 1.39 $
|
||||||
*
|
*
|
||||||
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
|
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
|
||||||
* IBM Corporation
|
* IBM Corporation
|
||||||
@ -35,10 +35,10 @@
|
|||||||
* These can be single devices or ranges of devices
|
* 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)) / \
|
#define __BL_DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \
|
||||||
(8*sizeof(long)))
|
(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;
|
typedef enum {add, free} range_action;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -46,21 +46,23 @@ typedef enum {add, free} range_action;
|
|||||||
* (Un-)blacklist the devices from-to
|
* (Un-)blacklist the devices from-to
|
||||||
*/
|
*/
|
||||||
static inline void
|
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)
|
if (!to)
|
||||||
to = from;
|
to = from;
|
||||||
|
|
||||||
if (from > to || to > __MAX_SUBCHANNEL) {
|
if (from > to || to > __MAX_SUBCHANNEL || ssid > __MAX_SSID) {
|
||||||
printk (KERN_WARNING "Invalid blacklist range "
|
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;
|
return;
|
||||||
}
|
}
|
||||||
for (; from <= to; from++) {
|
for (; from <= to; from++) {
|
||||||
if (action == add)
|
if (action == add)
|
||||||
set_bit (from, bl_dev);
|
set_bit (from, bl_dev[ssid]);
|
||||||
else
|
else
|
||||||
clear_bit (from, bl_dev);
|
clear_bit (from, bl_dev[ssid]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +72,7 @@ blacklist_range (range_action action, unsigned int from, unsigned int to)
|
|||||||
* Shamelessly grabbed from dasd_devmap.c.
|
* Shamelessly grabbed from dasd_devmap.c.
|
||||||
*/
|
*/
|
||||||
static inline int
|
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;
|
int val, old_style;
|
||||||
char *sav;
|
char *sav;
|
||||||
@ -87,7 +89,7 @@ blacklist_busid(char **str, int *id0, int *id1, int *devno)
|
|||||||
goto confused;
|
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 = *ssid = 0;
|
||||||
if (val < 0 || val > 0xffff)
|
if (val < 0 || val > 0xffff)
|
||||||
goto confused;
|
goto confused;
|
||||||
*devno = val;
|
*devno = val;
|
||||||
@ -106,7 +108,7 @@ blacklist_busid(char **str, int *id0, int *id1, int *devno)
|
|||||||
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] != '.')
|
||||||
goto confused;
|
goto confused;
|
||||||
*id1 = val;
|
*ssid = val;
|
||||||
if (!isxdigit((*str)[0])) /* We require at least one hex digit */
|
if (!isxdigit((*str)[0])) /* We require at least one hex digit */
|
||||||
goto confused;
|
goto confused;
|
||||||
val = simple_strtoul(*str, str, 16);
|
val = simple_strtoul(*str, str, 16);
|
||||||
@ -126,7 +128,7 @@ blacklist_busid(char **str, int *id0, int *id1, int *devno)
|
|||||||
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_ssid, to_ssid;
|
||||||
|
|
||||||
while (*str != 0 && *str != '\n') {
|
while (*str != 0 && *str != '\n') {
|
||||||
range_action ra = action;
|
range_action ra = action;
|
||||||
@ -143,23 +145,25 @@ blacklist_parse_parameters (char *str, range_action action)
|
|||||||
*/
|
*/
|
||||||
if (strncmp(str,"all,",4) == 0 || strcmp(str,"all") == 0 ||
|
if (strncmp(str,"all,",4) == 0 || strcmp(str,"all") == 0 ||
|
||||||
strncmp(str,"all\n",4) == 0 || strncmp(str,"all ",4) == 0) {
|
strncmp(str,"all\n",4) == 0 || strncmp(str,"all ",4) == 0) {
|
||||||
from = 0;
|
int j;
|
||||||
to = __MAX_SUBCHANNEL;
|
|
||||||
str += 3;
|
str += 3;
|
||||||
|
for (j=0; j <= __MAX_SSID; j++)
|
||||||
|
blacklist_range(ra, 0, __MAX_SUBCHANNEL, j);
|
||||||
} else {
|
} else {
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = blacklist_busid(&str, &from_id0,
|
rc = blacklist_busid(&str, &from_id0,
|
||||||
&from_id1, &from);
|
&from_ssid, &from);
|
||||||
if (rc)
|
if (rc)
|
||||||
continue;
|
continue;
|
||||||
to = from;
|
to = from;
|
||||||
to_id0 = from_id0;
|
to_id0 = from_id0;
|
||||||
to_id1 = from_id1;
|
to_ssid = from_ssid;
|
||||||
if (*str == '-') {
|
if (*str == '-') {
|
||||||
str++;
|
str++;
|
||||||
rc = blacklist_busid(&str, &to_id0,
|
rc = blacklist_busid(&str, &to_id0,
|
||||||
&to_id1, &to);
|
&to_ssid, &to);
|
||||||
if (rc)
|
if (rc)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -169,18 +173,19 @@ blacklist_parse_parameters (char *str, range_action action)
|
|||||||
strsep(&str, ",\n"));
|
strsep(&str, ",\n"));
|
||||||
continue;
|
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 "
|
printk(KERN_WARNING "invalid cio_ignore range "
|
||||||
"%x.%x.%04x-%x.%x.%04x\n",
|
"%x.%x.%04x-%x.%x.%04x\n",
|
||||||
from_id0, from_id1, from,
|
from_id0, from_ssid, from,
|
||||||
to_id0, to_id1, to);
|
to_id0, to_ssid, to);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/* 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 %x.%x.%04x to %x.%x.%04x\n",
|
||||||
blacklist_range (ra, from, to);
|
from_id0, from_ssid, from, to_id0, to_ssid, to);
|
||||||
|
blacklist_range (ra, from, to, to_ssid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -214,9 +219,9 @@ __setup ("cio_ignore=", blacklist_setup);
|
|||||||
* Used by validate_subchannel()
|
* Used by validate_subchannel()
|
||||||
*/
|
*/
|
||||||
int
|
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
|
#ifdef CONFIG_PROC_FS
|
||||||
@ -283,6 +288,7 @@ blacklist_parse_proc_parameters (char *buf)
|
|||||||
/* Iterator struct for all devices. */
|
/* Iterator struct for all devices. */
|
||||||
struct ccwdev_iter {
|
struct ccwdev_iter {
|
||||||
int devno;
|
int devno;
|
||||||
|
int ssid;
|
||||||
int in_range;
|
int in_range;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -291,13 +297,14 @@ cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset)
|
|||||||
{
|
{
|
||||||
struct ccwdev_iter *iter;
|
struct ccwdev_iter *iter;
|
||||||
|
|
||||||
if (*offset > __MAX_SUBCHANNEL)
|
if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
|
||||||
return NULL;
|
return NULL;
|
||||||
iter = kmalloc(sizeof(struct ccwdev_iter), GFP_KERNEL);
|
iter = kmalloc(sizeof(struct ccwdev_iter), GFP_KERNEL);
|
||||||
if (!iter)
|
if (!iter)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
memset(iter, 0, sizeof(struct ccwdev_iter));
|
memset(iter, 0, sizeof(struct ccwdev_iter));
|
||||||
iter->devno = *offset;
|
iter->ssid = *offset / (__MAX_SUBCHANNEL + 1);
|
||||||
|
iter->devno = *offset % (__MAX_SUBCHANNEL + 1);
|
||||||
return iter;
|
return iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,9 +320,15 @@ cio_ignore_proc_seq_next(struct seq_file *s, void *it, loff_t *offset)
|
|||||||
{
|
{
|
||||||
struct ccwdev_iter *iter;
|
struct ccwdev_iter *iter;
|
||||||
|
|
||||||
if (*offset > __MAX_SUBCHANNEL)
|
if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
|
||||||
return NULL;
|
return NULL;
|
||||||
iter = (struct ccwdev_iter *)it;
|
iter = (struct ccwdev_iter *)it;
|
||||||
|
if (iter->devno == __MAX_SUBCHANNEL) {
|
||||||
|
iter->devno = 0;
|
||||||
|
iter->ssid++;
|
||||||
|
if (iter->ssid > __MAX_SSID)
|
||||||
|
return NULL;
|
||||||
|
} else
|
||||||
iter->devno++;
|
iter->devno++;
|
||||||
(*offset)++;
|
(*offset)++;
|
||||||
return iter;
|
return iter;
|
||||||
@ -327,23 +340,24 @@ cio_ignore_proc_seq_show(struct seq_file *s, void *it)
|
|||||||
struct ccwdev_iter *iter;
|
struct ccwdev_iter *iter;
|
||||||
|
|
||||||
iter = (struct ccwdev_iter *)it;
|
iter = (struct ccwdev_iter *)it;
|
||||||
if (!is_blacklisted(iter->devno))
|
if (!is_blacklisted(iter->ssid, iter->devno))
|
||||||
/* Not blacklisted, nothing to output. */
|
/* Not blacklisted, nothing to output. */
|
||||||
return 0;
|
return 0;
|
||||||
if (!iter->in_range) {
|
if (!iter->in_range) {
|
||||||
/* First device in range. */
|
/* First device in range. */
|
||||||
if ((iter->devno == __MAX_SUBCHANNEL) ||
|
if ((iter->devno == __MAX_SUBCHANNEL) ||
|
||||||
!is_blacklisted(iter->devno + 1))
|
!is_blacklisted(iter->ssid, iter->devno + 1))
|
||||||
/* Singular device. */
|
/* 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;
|
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) ||
|
if ((iter->devno == __MAX_SUBCHANNEL) ||
|
||||||
!is_blacklisted(iter->devno + 1)) {
|
!is_blacklisted(iter->ssid, iter->devno + 1)) {
|
||||||
/* Last device in range. */
|
/* Last device in range. */
|
||||||
iter->in_range = 0;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#ifndef S390_BLACKLIST_H
|
#ifndef S390_BLACKLIST_H
|
||||||
#define S390_BLACKLIST_H
|
#define S390_BLACKLIST_H
|
||||||
|
|
||||||
extern int is_blacklisted (int devno);
|
extern int is_blacklisted (int ssid, int devno);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* drivers/s390/cio/chsc.c
|
* drivers/s390/cio/chsc.c
|
||||||
* S/390 common I/O routines -- channel subsystem call
|
* S/390 common I/O routines -- channel subsystem call
|
||||||
* $Revision: 1.120 $
|
* $Revision: 1.126 $
|
||||||
*
|
*
|
||||||
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
|
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
|
||||||
* IBM Corporation
|
* IBM Corporation
|
||||||
@ -75,7 +75,9 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page)
|
|||||||
|
|
||||||
struct {
|
struct {
|
||||||
struct chsc_header request;
|
struct chsc_header request;
|
||||||
u16 reserved1;
|
u16 reserved1a:10;
|
||||||
|
u16 ssid:2;
|
||||||
|
u16 reserved1b:4;
|
||||||
u16 f_sch; /* first subchannel */
|
u16 f_sch; /* first subchannel */
|
||||||
u16 reserved2;
|
u16 reserved2;
|
||||||
u16 l_sch; /* last subchannel */
|
u16 l_sch; /* last subchannel */
|
||||||
@ -102,6 +104,7 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page)
|
|||||||
.code = 0x0004,
|
.code = 0x0004,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ssd_area->ssid = sch->schid.ssid;
|
||||||
ssd_area->f_sch = sch->schid.sch_no;
|
ssd_area->f_sch = sch->schid.sch_no;
|
||||||
ssd_area->l_sch = sch->schid.sch_no;
|
ssd_area->l_sch = sch->schid.sch_no;
|
||||||
|
|
||||||
@ -145,8 +148,8 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page)
|
|||||||
*/
|
*/
|
||||||
if (ssd_area->st > 3) { /* uhm, that looks strange... */
|
if (ssd_area->st > 3) { /* uhm, that looks strange... */
|
||||||
CIO_CRW_EVENT(0, "Strange subchannel type %d"
|
CIO_CRW_EVENT(0, "Strange subchannel type %d"
|
||||||
" for sch %04x\n", ssd_area->st,
|
" for sch 0.%x.%04x\n", ssd_area->st,
|
||||||
sch->schid.sch_no);
|
sch->schid.ssid, sch->schid.sch_no);
|
||||||
/*
|
/*
|
||||||
* There may have been a new subchannel type defined in the
|
* There may have been a new subchannel type defined in the
|
||||||
* time since this code was written; since we don't know which
|
* time since this code was written; since we don't know which
|
||||||
@ -155,8 +158,9 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page)
|
|||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
const char *type[4] = {"I/O", "chsc", "message", "ADM"};
|
const char *type[4] = {"I/O", "chsc", "message", "ADM"};
|
||||||
CIO_CRW_EVENT(6, "ssd: sch %04x is %s subchannel\n",
|
CIO_CRW_EVENT(6, "ssd: sch 0.%x.%04x is %s subchannel\n",
|
||||||
sch->schid.sch_no, type[ssd_area->st]);
|
sch->schid.ssid, sch->schid.sch_no,
|
||||||
|
type[ssd_area->st]);
|
||||||
|
|
||||||
sch->ssd_info.valid = 1;
|
sch->ssd_info.valid = 1;
|
||||||
sch->ssd_info.type = ssd_area->st;
|
sch->ssd_info.type = ssd_area->st;
|
||||||
@ -364,7 +368,7 @@ s390_process_res_acc_new_sch(struct subchannel_id schid)
|
|||||||
* that beast may be on we'll have to do a stsch
|
* that beast may be on we'll have to do a stsch
|
||||||
* on all devices, grr...
|
* on all devices, grr...
|
||||||
*/
|
*/
|
||||||
if (stsch(schid, &schib))
|
if (stsch_err(schid, &schib))
|
||||||
/* We're through */
|
/* We're through */
|
||||||
return need_rescan ? -EAGAIN : -ENXIO;
|
return need_rescan ? -EAGAIN : -ENXIO;
|
||||||
|
|
||||||
@ -818,7 +822,7 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data)
|
|||||||
put_device(&sch->dev);
|
put_device(&sch->dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (stsch(schid, &schib))
|
if (stsch_err(schid, &schib))
|
||||||
/* We're through */
|
/* We're through */
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
/* Put it on the slow path. */
|
/* Put it on the slow path. */
|
||||||
@ -1078,6 +1082,54 @@ chsc_alloc_sei_area(void)
|
|||||||
return (sei_page ? 0 : -ENOMEM);
|
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);
|
subsys_initcall(chsc_alloc_sei_area);
|
||||||
|
|
||||||
struct css_general_char css_general_characteristics;
|
struct css_general_char css_general_characteristics;
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#define CHSC_SEI_ACC_LINKADDR 2
|
#define CHSC_SEI_ACC_LINKADDR 2
|
||||||
#define CHSC_SEI_ACC_FULLLINKADDR 3
|
#define CHSC_SEI_ACC_FULLLINKADDR 3
|
||||||
|
|
||||||
|
#define CHSC_SDA_OC_MSS 0x2
|
||||||
|
|
||||||
struct chsc_header {
|
struct chsc_header {
|
||||||
u16 length;
|
u16 length;
|
||||||
u16 code;
|
u16 code;
|
||||||
@ -64,6 +66,8 @@ extern int css_characteristics_avail;
|
|||||||
|
|
||||||
extern void *chsc_get_chp_desc(struct subchannel*, int);
|
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)
|
#define to_channelpath(dev) container_of(dev, struct channel_path, dev)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* 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.135 $
|
* $Revision: 1.138 $
|
||||||
*
|
*
|
||||||
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
|
* Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
|
||||||
* IBM Corporation
|
* IBM Corporation
|
||||||
@ -166,7 +166,8 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
|
|||||||
stsch (sch->schid, &sch->schib);
|
stsch (sch->schid, &sch->schib);
|
||||||
|
|
||||||
CIO_MSG_EVENT(0, "cio_start: 'not oper' status for "
|
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);
|
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));
|
||||||
@ -522,15 +523,18 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
|
|||||||
spin_lock_init(&sch->lock);
|
spin_lock_init(&sch->lock);
|
||||||
|
|
||||||
/* Set a name for the subchannel */
|
/* 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)
|
* The first subchannel that is not-operational (ccode==3)
|
||||||
* indicates that there aren't any more devices available.
|
* 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)
|
if (ccode)
|
||||||
return -ENXIO;
|
return (ccode == 3) ? -ENXIO : ccode;
|
||||||
|
|
||||||
sch->schid = schid;
|
sch->schid = schid;
|
||||||
/* Copy subchannel type from path management control word. */
|
/* Copy subchannel type from path management control word. */
|
||||||
@ -541,9 +545,9 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
|
|||||||
*/
|
*/
|
||||||
if (sch->st != 0) {
|
if (sch->st != 0) {
|
||||||
CIO_DEBUG(KERN_INFO, 0,
|
CIO_DEBUG(KERN_INFO, 0,
|
||||||
"Subchannel %04X reports "
|
"Subchannel 0.%x.%04x reports "
|
||||||
"non-I/O subchannel type %04X\n",
|
"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. */
|
/* We stop here for non-io subchannels. */
|
||||||
return sch->st;
|
return sch->st;
|
||||||
}
|
}
|
||||||
@ -554,16 +558,18 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
/* Devno is valid. */
|
/* 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
|
* This device must not be known to Linux. So we simply
|
||||||
* say that there is no device and return ENODEV.
|
* say that there is no device and return ENODEV.
|
||||||
*/
|
*/
|
||||||
CIO_MSG_EVENT(0, "Blacklisted device detected "
|
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;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
sch->opm = 0xff;
|
sch->opm = 0xff;
|
||||||
|
if (!cio_is_console(sch->schid))
|
||||||
chsc_validate_chpids(sch);
|
chsc_validate_chpids(sch);
|
||||||
sch->lpm = sch->schib.pmcw.pim &
|
sch->lpm = sch->schib.pmcw.pim &
|
||||||
sch->schib.pmcw.pam &
|
sch->schib.pmcw.pam &
|
||||||
@ -571,9 +577,10 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
|
|||||||
sch->opm;
|
sch->opm;
|
||||||
|
|
||||||
CIO_DEBUG(KERN_INFO, 0,
|
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",
|
" - 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);
|
sch->schib.pmcw.pam, sch->schib.pmcw.pom);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -693,7 +700,7 @@ wait_cons_dev (void)
|
|||||||
static int
|
static int
|
||||||
cio_test_for_console(struct subchannel_id schid, void *data)
|
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;
|
return -ENXIO;
|
||||||
if (console_subchannel.schib.pmcw.dnv &&
|
if (console_subchannel.schib.pmcw.dnv &&
|
||||||
console_subchannel.schib.pmcw.dev ==
|
console_subchannel.schib.pmcw.dev ==
|
||||||
@ -841,7 +848,7 @@ __shutdown_subchannel_easy(struct subchannel_id schid, void *data)
|
|||||||
{
|
{
|
||||||
struct schib schib;
|
struct schib schib;
|
||||||
|
|
||||||
if (stsch(schid, &schib))
|
if (stsch_err(schid, &schib))
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
if (!schib.pmcw.ena)
|
if (!schib.pmcw.ena)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* drivers/s390/cio/css.c
|
* drivers/s390/cio/css.c
|
||||||
* driver for channel subsystem
|
* driver for channel subsystem
|
||||||
* $Revision: 1.85 $
|
* $Revision: 1.93 $
|
||||||
*
|
*
|
||||||
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
|
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
|
||||||
* IBM Corporation
|
* IBM Corporation
|
||||||
@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
int need_rescan = 0;
|
int need_rescan = 0;
|
||||||
int css_init_done = 0;
|
int css_init_done = 0;
|
||||||
|
static int max_ssid = 0;
|
||||||
|
|
||||||
struct channel_subsystem *css[__MAX_CSSID + 1];
|
struct channel_subsystem *css[__MAX_CSSID + 1];
|
||||||
|
|
||||||
@ -36,11 +37,14 @@ for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data)
|
|||||||
|
|
||||||
init_subchannel_id(&schid);
|
init_subchannel_id(&schid);
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
|
do {
|
||||||
do {
|
do {
|
||||||
ret = fn(schid, data);
|
ret = fn(schid, data);
|
||||||
if (ret)
|
if (ret)
|
||||||
break;
|
break;
|
||||||
} while (schid.sch_no++ < __MAX_SUBCHANNEL);
|
} while (schid.sch_no++ < __MAX_SUBCHANNEL);
|
||||||
|
schid.sch_no = 0;
|
||||||
|
} while (schid.ssid++ < max_ssid);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,8 +209,8 @@ css_evaluate_subchannel(struct subchannel_id schid, int slow)
|
|||||||
return -EAGAIN; /* Will be done on the slow path. */
|
return -EAGAIN; /* Will be done on the slow path. */
|
||||||
}
|
}
|
||||||
event = css_get_subchannel_status(sch, schid);
|
event = css_get_subchannel_status(sch, schid);
|
||||||
CIO_MSG_EVENT(4, "Evaluating schid %04x, event %d, %s, %s path.\n",
|
CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n",
|
||||||
schid.sch_no, event,
|
schid.ssid, schid.sch_no, event,
|
||||||
sch?(disc?"disconnected":"normal"):"unknown",
|
sch?(disc?"disconnected":"normal"):"unknown",
|
||||||
slow?"slow":"fast");
|
slow?"slow":"fast");
|
||||||
switch (event) {
|
switch (event) {
|
||||||
@ -352,19 +356,23 @@ css_reiterate_subchannels(void)
|
|||||||
* Called from the machine check handler for subchannel report words.
|
* Called from the machine check handler for subchannel report words.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
css_process_crw(int irq)
|
css_process_crw(int rsid1, int rsid2)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct subchannel_id mchk_schid;
|
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)
|
if (need_rescan)
|
||||||
/* We need to iterate all subchannels anyway. */
|
/* We need to iterate all subchannels anyway. */
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
init_subchannel_id(&mchk_schid);
|
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
|
* 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
|
||||||
@ -465,12 +473,23 @@ init_channel_subsystem (void)
|
|||||||
if ((ret = bus_register(&css_bus_type)))
|
if ((ret = bus_register(&css_bus_type)))
|
||||||
goto out;
|
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. */
|
/* Setup css structure. */
|
||||||
for (i = 0; i <= __MAX_CSSID; i++) {
|
for (i = 0; i <= __MAX_CSSID; i++) {
|
||||||
css[i] = kmalloc(sizeof(struct channel_subsystem), GFP_KERNEL);
|
css[i] = kmalloc(sizeof(struct channel_subsystem), GFP_KERNEL);
|
||||||
if (!css[i]) {
|
if (!css[i]) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out_bus;
|
goto out_unregister;
|
||||||
}
|
}
|
||||||
setup_css(i);
|
setup_css(i);
|
||||||
ret = device_register(&css[i]->device);
|
ret = device_register(&css[i]->device);
|
||||||
@ -485,11 +504,12 @@ init_channel_subsystem (void)
|
|||||||
return 0;
|
return 0;
|
||||||
out_free:
|
out_free:
|
||||||
kfree(css[i]);
|
kfree(css[i]);
|
||||||
out_bus:
|
out_unregister:
|
||||||
while (i > 0) {
|
while (i > 0) {
|
||||||
i--;
|
i--;
|
||||||
device_unregister(&css[i]->device);
|
device_unregister(&css[i]->device);
|
||||||
}
|
}
|
||||||
|
out_bus:
|
||||||
bus_unregister(&css_bus_type);
|
bus_unregister(&css_bus_type);
|
||||||
out:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -77,6 +77,7 @@ struct ccw_device_private {
|
|||||||
unsigned long registered;
|
unsigned long registered;
|
||||||
__u16 devno; /* device number */
|
__u16 devno; /* device number */
|
||||||
__u16 sch_no; /* subchannel number */
|
__u16 sch_no; /* subchannel number */
|
||||||
|
__u8 ssid; /* subchannel set id */
|
||||||
__u8 imask; /* lpm mask for SNID/SID/SPGID */
|
__u8 imask; /* lpm mask for SNID/SID/SPGID */
|
||||||
int iretry; /* retry counter SNID/SID/SPGID */
|
int iretry; /* retry counter SNID/SID/SPGID */
|
||||||
struct {
|
struct {
|
||||||
@ -135,6 +136,7 @@ extern int css_init_done;
|
|||||||
extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
|
extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
|
||||||
|
|
||||||
#define __MAX_SUBCHANNEL 65535
|
#define __MAX_SUBCHANNEL 65535
|
||||||
|
#define __MAX_SSID 3
|
||||||
#define __MAX_CHPID 255
|
#define __MAX_CHPID 255
|
||||||
#define __MAX_CSSID 0
|
#define __MAX_CSSID 0
|
||||||
|
|
||||||
|
@ -536,6 +536,7 @@ ccw_device_register(struct ccw_device *cdev)
|
|||||||
|
|
||||||
struct match_data {
|
struct match_data {
|
||||||
unsigned int devno;
|
unsigned int devno;
|
||||||
|
unsigned int ssid;
|
||||||
struct ccw_device * sibling;
|
struct ccw_device * sibling;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -548,6 +549,7 @@ match_devno(struct device * dev, void * data)
|
|||||||
cdev = to_ccwdev(dev);
|
cdev = to_ccwdev(dev);
|
||||||
if ((cdev->private->state == DEV_STATE_DISCONNECTED) &&
|
if ((cdev->private->state == DEV_STATE_DISCONNECTED) &&
|
||||||
(cdev->private->devno == d->devno) &&
|
(cdev->private->devno == d->devno) &&
|
||||||
|
(cdev->private->ssid == d->ssid) &&
|
||||||
(cdev != d->sibling)) {
|
(cdev != d->sibling)) {
|
||||||
cdev->private->state = DEV_STATE_NOT_OPER;
|
cdev->private->state = DEV_STATE_NOT_OPER;
|
||||||
return 1;
|
return 1;
|
||||||
@ -556,11 +558,13 @@ match_devno(struct device * dev, void * data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct ccw_device *
|
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 device *dev;
|
||||||
struct match_data data = {
|
struct match_data data = {
|
||||||
.devno = devno,
|
.devno = devno,
|
||||||
|
.ssid = ssid,
|
||||||
.sibling = sibling,
|
.sibling = sibling,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -616,7 +620,7 @@ ccw_device_do_unreg_rereg(void *data)
|
|||||||
|
|
||||||
need_rename = 1;
|
need_rename = 1;
|
||||||
other_cdev = get_disc_ccwdev_by_devno(sch->schib.pmcw.dev,
|
other_cdev = get_disc_ccwdev_by_devno(sch->schib.pmcw.dev,
|
||||||
cdev);
|
sch->schid.ssid, cdev);
|
||||||
if (other_cdev) {
|
if (other_cdev) {
|
||||||
struct subchannel *other_sch;
|
struct subchannel *other_sch;
|
||||||
|
|
||||||
@ -639,8 +643,8 @@ ccw_device_do_unreg_rereg(void *data)
|
|||||||
if (test_and_clear_bit(1, &cdev->private->registered))
|
if (test_and_clear_bit(1, &cdev->private->registered))
|
||||||
device_del(&cdev->dev);
|
device_del(&cdev->dev);
|
||||||
if (need_rename)
|
if (need_rename)
|
||||||
snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.0.%04x",
|
snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x",
|
||||||
sch->schib.pmcw.dev);
|
sch->schid.ssid, sch->schib.pmcw.dev);
|
||||||
PREPARE_WORK(&cdev->private->kick_work,
|
PREPARE_WORK(&cdev->private->kick_work,
|
||||||
ccw_device_add_changed, (void *)cdev);
|
ccw_device_add_changed, (void *)cdev);
|
||||||
queue_work(ccw_device_work, &cdev->private->kick_work);
|
queue_work(ccw_device_work, &cdev->private->kick_work);
|
||||||
@ -769,9 +773,11 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
|
|||||||
sch->dev.driver_data = cdev;
|
sch->dev.driver_data = cdev;
|
||||||
sch->driver = &io_subchannel_driver;
|
sch->driver = &io_subchannel_driver;
|
||||||
cdev->ccwlock = &sch->lock;
|
cdev->ccwlock = &sch->lock;
|
||||||
|
|
||||||
/* Init private data. */
|
/* Init private data. */
|
||||||
priv = cdev->private;
|
priv = cdev->private;
|
||||||
priv->devno = sch->schib.pmcw.dev;
|
priv->devno = sch->schib.pmcw.dev;
|
||||||
|
priv->ssid = sch->schid.ssid;
|
||||||
priv->sch_no = sch->schid.sch_no;
|
priv->sch_no = sch->schid.sch_no;
|
||||||
priv->state = DEV_STATE_NOT_OPER;
|
priv->state = DEV_STATE_NOT_OPER;
|
||||||
INIT_LIST_HEAD(&priv->cmb_list);
|
INIT_LIST_HEAD(&priv->cmb_list);
|
||||||
@ -779,8 +785,8 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
|
|||||||
init_timer(&priv->timer);
|
init_timer(&priv->timer);
|
||||||
|
|
||||||
/* Set an initial name for the device. */
|
/* Set an initial name for the device. */
|
||||||
snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.0.%04x",
|
snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x",
|
||||||
sch->schib.pmcw.dev);
|
sch->schid.ssid, sch->schib.pmcw.dev);
|
||||||
|
|
||||||
/* Increase counter of devices currently in recognition. */
|
/* Increase counter of devices currently in recognition. */
|
||||||
atomic_inc(&ccw_device_init_count);
|
atomic_inc(&ccw_device_init_count);
|
||||||
|
@ -257,8 +257,9 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
|
|||||||
switch (state) {
|
switch (state) {
|
||||||
case DEV_STATE_NOT_OPER:
|
case DEV_STATE_NOT_OPER:
|
||||||
CIO_DEBUG(KERN_WARNING, 2,
|
CIO_DEBUG(KERN_WARNING, 2,
|
||||||
"SenseID : unknown device %04x on subchannel %04x\n",
|
"SenseID : unknown device %04x on subchannel "
|
||||||
cdev->private->devno, sch->schid.sch_no);
|
"0.%x.%04x\n", cdev->private->devno,
|
||||||
|
sch->schid.ssid, sch->schid.sch_no);
|
||||||
break;
|
break;
|
||||||
case DEV_STATE_OFFLINE:
|
case DEV_STATE_OFFLINE:
|
||||||
if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) {
|
if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) {
|
||||||
@ -282,16 +283,18 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Issue device info message. */
|
/* 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 = "
|
"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.cu_type, cdev->id.cu_model,
|
||||||
cdev->id.dev_type, cdev->id.dev_model);
|
cdev->id.dev_type, cdev->id.dev_model);
|
||||||
break;
|
break;
|
||||||
case DEV_STATE_BOXED:
|
case DEV_STATE_BOXED:
|
||||||
CIO_DEBUG(KERN_WARNING, 2,
|
CIO_DEBUG(KERN_WARNING, 2,
|
||||||
"SenseID : boxed device %04x on subchannel %04x\n",
|
"SenseID : boxed device %04x on subchannel "
|
||||||
cdev->private->devno, sch->schid.sch_no);
|
"0.%x.%04x\n", cdev->private->devno,
|
||||||
|
sch->schid.ssid, sch->schid.sch_no);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cdev->private->state = state;
|
cdev->private->state = state;
|
||||||
|
@ -256,16 +256,17 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
|
|||||||
* sense id information. So, for intervention required,
|
* sense id information. So, for intervention required,
|
||||||
* we use the "whack it until it talks" strategy...
|
* we use the "whack it until it talks" strategy...
|
||||||
*/
|
*/
|
||||||
CIO_MSG_EVENT(2, "SenseID : device %04x on Subchannel %04x "
|
CIO_MSG_EVENT(2, "SenseID : device %04x on Subchannel "
|
||||||
"reports cmd reject\n",
|
"0.%x.%04x reports cmd reject\n",
|
||||||
cdev->private->devno, sch->schid.sch_no);
|
cdev->private->devno, sch->schid.ssid,
|
||||||
|
sch->schid.sch_no);
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
if (irb->esw.esw0.erw.cons) {
|
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 :"
|
"lpum %02X, cnt %02d, sns :"
|
||||||
" %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
|
" %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.sublog.lpum,
|
||||||
irb->esw.esw0.erw.scnt,
|
irb->esw.esw0.erw.scnt,
|
||||||
irb->ecw[0], irb->ecw[1],
|
irb->ecw[0], irb->ecw[1],
|
||||||
@ -277,16 +278,17 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
|
|||||||
if (irb->scsw.cc == 3) {
|
if (irb->scsw.cc == 3) {
|
||||||
if ((sch->orb.lpm &
|
if ((sch->orb.lpm &
|
||||||
sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
|
sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
|
||||||
CIO_MSG_EVENT(2, "SenseID : path %02X for device %04x on"
|
CIO_MSG_EVENT(2, "SenseID : path %02X for device %04x "
|
||||||
" subchannel %04x is 'not operational'\n",
|
"on subchannel 0.%x.%04x is "
|
||||||
sch->orb.lpm, cdev->private->devno,
|
"'not operational'\n", sch->orb.lpm,
|
||||||
|
cdev->private->devno, sch->schid.ssid,
|
||||||
sch->schid.sch_no);
|
sch->schid.sch_no);
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
}
|
}
|
||||||
/* Hmm, whatever happened, try again. */
|
/* Hmm, whatever happened, try again. */
|
||||||
CIO_MSG_EVENT(2, "SenseID : start_IO() for device %04x on "
|
CIO_MSG_EVENT(2, "SenseID : start_IO() for device %04x on "
|
||||||
"subchannel %04x returns status %02X%02X\n",
|
"subchannel 0.%x.%04x returns status %02X%02X\n",
|
||||||
cdev->private->devno, sch->schid.sch_no,
|
cdev->private->devno, sch->schid.ssid, sch->schid.sch_no,
|
||||||
irb->scsw.dstat, irb->scsw.cstat);
|
irb->scsw.dstat, irb->scsw.cstat);
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
@ -57,10 +57,10 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
|
|||||||
if (ret != -EACCES)
|
if (ret != -EACCES)
|
||||||
return ret;
|
return ret;
|
||||||
CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel "
|
CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel "
|
||||||
"%04x, lpm %02X, became 'not "
|
"0.%x.%04x, lpm %02X, became 'not "
|
||||||
"operational'\n",
|
"operational'\n",
|
||||||
cdev->private->devno, sch->schid.sch_no,
|
cdev->private->devno, sch->schid.ssid,
|
||||||
cdev->private->imask);
|
sch->schid.sch_no, cdev->private->imask);
|
||||||
|
|
||||||
}
|
}
|
||||||
cdev->private->imask >>= 1;
|
cdev->private->imask >>= 1;
|
||||||
@ -106,10 +106,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 %04x, unit check, "
|
CIO_MSG_EVENT(2, "SNID - device 0.%x.%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->private->devno,
|
cdev->private->ssid, 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],
|
||||||
@ -119,16 +119,17 @@ __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 %04x on Subchannel "
|
CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x,"
|
||||||
"%04x, lpm %02X, became 'not operational'\n",
|
" lpm %02X, became 'not operational'\n",
|
||||||
cdev->private->devno, sch->schid.sch_no,
|
cdev->private->devno, sch->schid.ssid,
|
||||||
sch->orb.lpm);
|
sch->schid.sch_no, 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 %04x on Subchannel %04x "
|
CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x "
|
||||||
"is reserved by someone else\n",
|
"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 -EUSERS;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -237,8 +238,9 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
|
|||||||
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 %04x on Subchannel "
|
CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel "
|
||||||
"%04x, lpm %02X, became 'not operational'\n",
|
"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);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,8 +262,10 @@ __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 %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",
|
"sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
|
||||||
|
cdev->private->ssid,
|
||||||
cdev->private->devno, 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],
|
||||||
@ -270,10 +274,10 @@ __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 %04x on Subchannel "
|
CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel 0.%x.%04x,"
|
||||||
"%04x, lpm %02X, became 'not operational'\n",
|
" lpm %02X, became 'not operational'\n",
|
||||||
cdev->private->devno, sch->schid.sch_no,
|
cdev->private->devno, sch->schid.ssid,
|
||||||
cdev->private->imask);
|
sch->schid.sch_no, cdev->private->imask);
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -36,9 +36,10 @@ ccw_device_msg_control_check(struct ccw_device *cdev, struct irb *irb)
|
|||||||
|
|
||||||
CIO_MSG_EVENT(0, "Channel-Check or Interface-Control-Check "
|
CIO_MSG_EVENT(0, "Channel-Check or Interface-Control-Check "
|
||||||
"received"
|
"received"
|
||||||
" ... device %04X on subchannel %04X, dev_stat "
|
" ... device %04x on subchannel 0.%x.%04x, dev_stat "
|
||||||
": %02X sch_stat : %02X\n",
|
": %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);
|
irb->scsw.dstat, irb->scsw.cstat);
|
||||||
|
|
||||||
if (irb->scsw.cc != 3) {
|
if (irb->scsw.cc != 3) {
|
||||||
@ -61,8 +62,9 @@ ccw_device_path_notoper(struct ccw_device *cdev)
|
|||||||
sch = to_subchannel(cdev->dev.parent);
|
sch = to_subchannel(cdev->dev.parent);
|
||||||
stsch (sch->schid, &sch->schib);
|
stsch (sch->schid, &sch->schib);
|
||||||
|
|
||||||
CIO_MSG_EVENT(0, "%s(%04x) - path(s) %02x are "
|
CIO_MSG_EVENT(0, "%s(0.%x.%04x) - path(s) %02x are "
|
||||||
"not operational \n", __FUNCTION__, sch->schid.sch_no,
|
"not operational \n", __FUNCTION__,
|
||||||
|
sch->schid.ssid, sch->schid.sch_no,
|
||||||
sch->schib.pmcw.pnom);
|
sch->schib.pmcw.pnom);
|
||||||
|
|
||||||
sch->lpm &= ~sch->schib.pmcw.pnom;
|
sch->lpm &= ~sch->schib.pmcw.pnom;
|
||||||
|
@ -38,6 +38,35 @@ static inline int stsch(struct subchannel_id schid,
|
|||||||
return ccode;
|
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,
|
static inline int msch(struct subchannel_id schid,
|
||||||
volatile struct schib *addr)
|
volatile struct schib *addr)
|
||||||
{
|
{
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
#include "ioasm.h"
|
#include "ioasm.h"
|
||||||
#include "chsc.h"
|
#include "chsc.h"
|
||||||
|
|
||||||
#define VERSION_QDIO_C "$Revision: 1.113 $"
|
#define VERSION_QDIO_C "$Revision: 1.114 $"
|
||||||
|
|
||||||
/****************** MODULE PARAMETER VARIABLES ********************/
|
/****************** MODULE PARAMETER VARIABLES ********************/
|
||||||
MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
|
MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
|
||||||
@ -2066,21 +2066,22 @@ qdio_timeout_handler(struct ccw_device *cdev)
|
|||||||
|
|
||||||
switch (irq_ptr->state) {
|
switch (irq_ptr->state) {
|
||||||
case QDIO_IRQ_STATE_INACTIVE:
|
case QDIO_IRQ_STATE_INACTIVE:
|
||||||
QDIO_PRINT_ERR("establish queues on irq %04x: timed out\n",
|
QDIO_PRINT_ERR("establish queues on irq 0.%x.%04x: timed out\n",
|
||||||
irq_ptr->schid.sch_no);
|
irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
|
||||||
QDIO_DBF_TEXT2(1,setup,"eq:timeo");
|
QDIO_DBF_TEXT2(1,setup,"eq:timeo");
|
||||||
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
|
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
|
||||||
break;
|
break;
|
||||||
case QDIO_IRQ_STATE_CLEANUP:
|
case QDIO_IRQ_STATE_CLEANUP:
|
||||||
QDIO_PRINT_INFO("Did not get interrupt on cleanup, irq=0x%x.\n",
|
QDIO_PRINT_INFO("Did not get interrupt on cleanup, "
|
||||||
irq_ptr->schid.sch_no);
|
"irq=0.%x.%x.\n",
|
||||||
|
irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
|
||||||
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
|
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
|
||||||
break;
|
break;
|
||||||
case QDIO_IRQ_STATE_ESTABLISHED:
|
case QDIO_IRQ_STATE_ESTABLISHED:
|
||||||
case QDIO_IRQ_STATE_ACTIVE:
|
case QDIO_IRQ_STATE_ACTIVE:
|
||||||
/* I/O has been terminated by common I/O layer. */
|
/* I/O has been terminated by common I/O layer. */
|
||||||
QDIO_PRINT_INFO("Queues on irq %04x killed by cio.\n",
|
QDIO_PRINT_INFO("Queues on irq 0.%x.%04x killed by cio.\n",
|
||||||
irq_ptr->schid.sch_no);
|
irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
|
||||||
QDIO_DBF_TEXT2(1, trace, "cio:term");
|
QDIO_DBF_TEXT2(1, trace, "cio:term");
|
||||||
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
|
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
|
||||||
if (get_device(&cdev->dev)) {
|
if (get_device(&cdev->dev)) {
|
||||||
@ -2273,7 +2274,9 @@ qdio_get_ssqd_information(struct qdio_irq *irq_ptr)
|
|||||||
unsigned char qdioac;
|
unsigned char qdioac;
|
||||||
struct {
|
struct {
|
||||||
struct chsc_header request;
|
struct chsc_header request;
|
||||||
u16 reserved1;
|
u16 reserved1:10;
|
||||||
|
u16 ssid:2;
|
||||||
|
u16 fmt:4;
|
||||||
u16 first_sch;
|
u16 first_sch;
|
||||||
u16 reserved2;
|
u16 reserved2;
|
||||||
u16 last_sch;
|
u16 last_sch;
|
||||||
@ -2318,12 +2321,13 @@ qdio_get_ssqd_information(struct qdio_irq *irq_ptr)
|
|||||||
};
|
};
|
||||||
ssqd_area->first_sch = irq_ptr->schid.sch_no;
|
ssqd_area->first_sch = irq_ptr->schid.sch_no;
|
||||||
ssqd_area->last_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);
|
result = chsc(ssqd_area);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
QDIO_PRINT_WARN("CHSC returned cc %i. Using all " \
|
QDIO_PRINT_WARN("CHSC returned cc %i. Using all " \
|
||||||
"SIGAs for sch x%x.\n",
|
"SIGAs for sch 0.%x.%x.\n", result,
|
||||||
result, irq_ptr->schid.sch_no);
|
irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
|
||||||
qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY ||
|
qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY ||
|
||||||
CHSC_FLAG_SIGA_OUTPUT_NECESSARY ||
|
CHSC_FLAG_SIGA_OUTPUT_NECESSARY ||
|
||||||
CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */
|
CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */
|
||||||
@ -2333,8 +2337,9 @@ qdio_get_ssqd_information(struct qdio_irq *irq_ptr)
|
|||||||
|
|
||||||
if (ssqd_area->response.code != QDIO_CHSC_RESPONSE_CODE_OK) {
|
if (ssqd_area->response.code != QDIO_CHSC_RESPONSE_CODE_OK) {
|
||||||
QDIO_PRINT_WARN("response upon checking SIGA needs " \
|
QDIO_PRINT_WARN("response upon checking SIGA needs " \
|
||||||
"is 0x%x. Using all SIGAs for sch x%x.\n",
|
"is 0x%x. Using all SIGAs for sch 0.%x.%x.\n",
|
||||||
ssqd_area->response.code, irq_ptr->schid.sch_no);
|
ssqd_area->response.code,
|
||||||
|
irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
|
||||||
qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY ||
|
qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY ||
|
||||||
CHSC_FLAG_SIGA_OUTPUT_NECESSARY ||
|
CHSC_FLAG_SIGA_OUTPUT_NECESSARY ||
|
||||||
CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */
|
CHSC_FLAG_SIGA_SYNC_NECESSARY; /* all flags set */
|
||||||
@ -2344,8 +2349,9 @@ qdio_get_ssqd_information(struct qdio_irq *irq_ptr)
|
|||||||
if (!(ssqd_area->flags & CHSC_FLAG_QDIO_CAPABILITY) ||
|
if (!(ssqd_area->flags & CHSC_FLAG_QDIO_CAPABILITY) ||
|
||||||
!(ssqd_area->flags & CHSC_FLAG_VALIDITY) ||
|
!(ssqd_area->flags & CHSC_FLAG_VALIDITY) ||
|
||||||
(ssqd_area->sch != irq_ptr->schid.sch_no)) {
|
(ssqd_area->sch != irq_ptr->schid.sch_no)) {
|
||||||
QDIO_PRINT_WARN("huh? problems checking out sch x%x... " \
|
QDIO_PRINT_WARN("huh? problems checking out sch 0.%x.%x... " \
|
||||||
"using all SIGAs.\n",irq_ptr->schid.sch_no);
|
"using all SIGAs.\n",
|
||||||
|
irq_ptr->schid.ssid, irq_ptr->schid.sch_no);
|
||||||
qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY |
|
qdioac = CHSC_FLAG_SIGA_INPUT_NECESSARY |
|
||||||
CHSC_FLAG_SIGA_OUTPUT_NECESSARY |
|
CHSC_FLAG_SIGA_OUTPUT_NECESSARY |
|
||||||
CHSC_FLAG_SIGA_SYNC_NECESSARY; /* worst case */
|
CHSC_FLAG_SIGA_SYNC_NECESSARY; /* worst case */
|
||||||
@ -2453,7 +2459,8 @@ tiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero)
|
|||||||
scssc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
|
scssc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
|
||||||
if (!scssc_area) {
|
if (!scssc_area) {
|
||||||
QDIO_PRINT_WARN("No memory for setting indicators on " \
|
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;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
scssc_area->request = (struct chsc_header) {
|
scssc_area->request = (struct chsc_header) {
|
||||||
@ -2479,8 +2486,9 @@ tiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero)
|
|||||||
|
|
||||||
result = chsc(scssc_area);
|
result = chsc(scssc_area);
|
||||||
if (result) {
|
if (result) {
|
||||||
QDIO_PRINT_WARN("could not set indicators on irq x%x, " \
|
QDIO_PRINT_WARN("could not set indicators on irq 0.%x.%x, " \
|
||||||
"cc=%i.\n",irq_ptr->schid.sch_no,result);
|
"cc=%i.\n",
|
||||||
|
irq_ptr->schid.ssid, irq_ptr->schid.sch_no,result);
|
||||||
result = -EIO;
|
result = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -2536,7 +2544,8 @@ tiqdio_set_delay_target(struct qdio_irq *irq_ptr, unsigned long delay_target)
|
|||||||
scsscf_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
|
scsscf_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
|
||||||
if (!scsscf_area) {
|
if (!scsscf_area) {
|
||||||
QDIO_PRINT_WARN("No memory for setting delay target on " \
|
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;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
scsscf_area->request = (struct chsc_header) {
|
scsscf_area->request = (struct chsc_header) {
|
||||||
@ -2548,8 +2557,9 @@ tiqdio_set_delay_target(struct qdio_irq *irq_ptr, unsigned long delay_target)
|
|||||||
|
|
||||||
result=chsc(scsscf_area);
|
result=chsc(scsscf_area);
|
||||||
if (result) {
|
if (result) {
|
||||||
QDIO_PRINT_WARN("could not set delay target on irq x%x, " \
|
QDIO_PRINT_WARN("could not set delay target on irq 0.%x.%x, " \
|
||||||
"cc=%i. Continuing.\n",irq_ptr->schid.sch_no,
|
"cc=%i. Continuing.\n",
|
||||||
|
irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
|
||||||
result);
|
result);
|
||||||
result = -EIO;
|
result = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
@ -2870,8 +2880,9 @@ qdio_establish_irq_check_for_errors(struct ccw_device *cdev, int cstat,
|
|||||||
QDIO_DBF_HEX2(0,trace,&dstat,sizeof(int));
|
QDIO_DBF_HEX2(0,trace,&dstat,sizeof(int));
|
||||||
QDIO_DBF_HEX2(0,trace,&cstat,sizeof(int));
|
QDIO_DBF_HEX2(0,trace,&cstat,sizeof(int));
|
||||||
QDIO_PRINT_ERR("received check condition on establish " \
|
QDIO_PRINT_ERR("received check condition on establish " \
|
||||||
"queues on irq 0x%x (cs=x%x, ds=x%x).\n",
|
"queues on irq 0.%x.%x (cs=x%x, ds=x%x).\n",
|
||||||
irq_ptr->schid.sch_no,cstat,dstat);
|
irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
|
||||||
|
cstat,dstat);
|
||||||
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ERR);
|
qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ERR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2879,9 +2890,10 @@ qdio_establish_irq_check_for_errors(struct ccw_device *cdev, int cstat,
|
|||||||
QDIO_DBF_TEXT2(1,setup,"eq:no de");
|
QDIO_DBF_TEXT2(1,setup,"eq:no de");
|
||||||
QDIO_DBF_HEX2(0,setup,&dstat, sizeof(dstat));
|
QDIO_DBF_HEX2(0,setup,&dstat, sizeof(dstat));
|
||||||
QDIO_DBF_HEX2(0,setup,&cstat, sizeof(cstat));
|
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",
|
"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);
|
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -2890,9 +2902,9 @@ qdio_establish_irq_check_for_errors(struct ccw_device *cdev, int cstat,
|
|||||||
QDIO_DBF_TEXT2(1,setup,"eq:badio");
|
QDIO_DBF_TEXT2(1,setup,"eq:badio");
|
||||||
QDIO_DBF_HEX2(0,setup,&dstat, sizeof(dstat));
|
QDIO_DBF_HEX2(0,setup,&dstat, sizeof(dstat));
|
||||||
QDIO_DBF_HEX2(0,setup,&cstat, sizeof(cstat));
|
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, "
|
"the following devstat: dstat=%02x, "
|
||||||
"cstat=%02x\n",
|
"cstat=%02x\n", irq_ptr->schid.ssid,
|
||||||
irq_ptr->schid.sch_no, dstat, cstat);
|
irq_ptr->schid.sch_no, dstat, cstat);
|
||||||
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
|
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
|
||||||
return 1;
|
return 1;
|
||||||
@ -3041,7 +3053,8 @@ int qdio_fill_irq(struct qdio_initialize *init_data)
|
|||||||
QDIO_DBF_HEX1(0,setup,&irq_ptr->dev_st_chg_ind,sizeof(void*));
|
QDIO_DBF_HEX1(0,setup,&irq_ptr->dev_st_chg_ind,sizeof(void*));
|
||||||
if (!irq_ptr->dev_st_chg_ind) {
|
if (!irq_ptr->dev_st_chg_ind) {
|
||||||
QDIO_PRINT_WARN("no indicator location available " \
|
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);
|
qdio_release_irq_memory(irq_ptr);
|
||||||
return -ENOBUFS;
|
return -ENOBUFS;
|
||||||
}
|
}
|
||||||
@ -3198,9 +3211,10 @@ qdio_establish(struct qdio_initialize *init_data)
|
|||||||
sprintf(dbf_text,"eq:io%4x",result);
|
sprintf(dbf_text,"eq:io%4x",result);
|
||||||
QDIO_DBF_TEXT2(1,setup,dbf_text);
|
QDIO_DBF_TEXT2(1,setup,dbf_text);
|
||||||
}
|
}
|
||||||
QDIO_PRINT_WARN("establish queues on irq %04x: do_IO " \
|
QDIO_PRINT_WARN("establish queues on irq 0.%x.%04x: do_IO " \
|
||||||
"returned %i, next try returned %i\n",
|
"returned %i, next try returned %i\n",
|
||||||
irq_ptr->schid.sch_no,result,result2);
|
irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
|
||||||
|
result, result2);
|
||||||
result=result2;
|
result=result2;
|
||||||
if (result)
|
if (result)
|
||||||
ccw_device_set_timeout(cdev, 0);
|
ccw_device_set_timeout(cdev, 0);
|
||||||
@ -3298,9 +3312,10 @@ qdio_activate(struct ccw_device *cdev, int flags)
|
|||||||
sprintf(dbf_text,"aq:io%4x",result);
|
sprintf(dbf_text,"aq:io%4x",result);
|
||||||
QDIO_DBF_TEXT2(1,setup,dbf_text);
|
QDIO_DBF_TEXT2(1,setup,dbf_text);
|
||||||
}
|
}
|
||||||
QDIO_PRINT_WARN("activate queues on irq %04x: do_IO " \
|
QDIO_PRINT_WARN("activate queues on irq 0.%x.%04x: do_IO " \
|
||||||
"returned %i, next try returned %i\n",
|
"returned %i, next try returned %i\n",
|
||||||
irq_ptr->schid.sch_no,result,result2);
|
irq_ptr->schid.ssid, irq_ptr->schid.sch_no,
|
||||||
|
result, result2);
|
||||||
result=result2;
|
result=result2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
#define S390_SCHID_H
|
#define S390_SCHID_H
|
||||||
|
|
||||||
struct subchannel_id {
|
struct subchannel_id {
|
||||||
__u32 reserved:15;
|
__u32 reserved:13;
|
||||||
|
__u32 ssid:2;
|
||||||
__u32 one:1;
|
__u32 one:1;
|
||||||
__u32 sch_no:16;
|
__u32 sch_no:16;
|
||||||
} __attribute__ ((packed,aligned(4)));
|
} __attribute__ ((packed,aligned(4)));
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
static struct semaphore m_sem;
|
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 chsc_process_crw(void);
|
||||||
extern int chp_process_crw(int, int);
|
extern int chp_process_crw(int, int);
|
||||||
extern void css_reiterate_subchannels(void);
|
extern void css_reiterate_subchannels(void);
|
||||||
@ -49,9 +49,10 @@ s390_handle_damage(char *msg)
|
|||||||
static int
|
static int
|
||||||
s390_collect_crw_info(void *param)
|
s390_collect_crw_info(void *param)
|
||||||
{
|
{
|
||||||
struct crw crw;
|
struct crw crw[2];
|
||||||
int ccode, ret, slow;
|
int ccode, ret, slow;
|
||||||
struct semaphore *sem;
|
struct semaphore *sem;
|
||||||
|
unsigned int chain;
|
||||||
|
|
||||||
sem = (struct semaphore *)param;
|
sem = (struct semaphore *)param;
|
||||||
/* Set a nice name. */
|
/* Set a nice name. */
|
||||||
@ -59,25 +60,50 @@ s390_collect_crw_info(void *param)
|
|||||||
repeat:
|
repeat:
|
||||||
down_interruptible(sem);
|
down_interruptible(sem);
|
||||||
slow = 0;
|
slow = 0;
|
||||||
|
chain = 0;
|
||||||
while (1) {
|
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)
|
if (ccode != 0)
|
||||||
break;
|
break;
|
||||||
DBG(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, "
|
DBG(KERN_DEBUG "crw_info : CRW reports slct=%d, oflw=%d, "
|
||||||
"chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
|
"chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
|
||||||
crw.slct, crw.oflw, crw.chn, crw.rsc, crw.anc,
|
crw[chain].slct, crw[chain].oflw, crw[chain].chn,
|
||||||
crw.erc, crw.rsid);
|
crw[chain].rsc, crw[chain].anc, crw[chain].erc,
|
||||||
|
crw[chain].rsid);
|
||||||
/* Check for overflows. */
|
/* Check for overflows. */
|
||||||
if (crw.oflw) {
|
if (crw[chain].oflw) {
|
||||||
pr_debug("%s: crw overflow detected!\n", __FUNCTION__);
|
pr_debug("%s: crw overflow detected!\n", __FUNCTION__);
|
||||||
css_reiterate_subchannels();
|
css_reiterate_subchannels();
|
||||||
|
chain = 0;
|
||||||
slow = 1;
|
slow = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
switch (crw.rsc) {
|
switch (crw[chain].rsc) {
|
||||||
case CRW_RSC_SCH:
|
case CRW_RSC_SCH:
|
||||||
pr_debug("source is subchannel %04X\n", crw.rsid);
|
if (crw[0].chn && !chain)
|
||||||
ret = css_process_crw (crw.rsid);
|
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)
|
if (ret == -EAGAIN)
|
||||||
slow = 1;
|
slow = 1;
|
||||||
break;
|
break;
|
||||||
@ -85,18 +111,18 @@ s390_collect_crw_info(void *param)
|
|||||||
pr_debug("source is monitoring facility\n");
|
pr_debug("source is monitoring facility\n");
|
||||||
break;
|
break;
|
||||||
case CRW_RSC_CPATH:
|
case CRW_RSC_CPATH:
|
||||||
pr_debug("source is channel path %02X\n", crw.rsid);
|
pr_debug("source is channel path %02X\n", crw[0].rsid);
|
||||||
switch (crw.erc) {
|
switch (crw[0].erc) {
|
||||||
case CRW_ERC_IPARM: /* Path has come. */
|
case CRW_ERC_IPARM: /* Path has come. */
|
||||||
ret = chp_process_crw(crw.rsid, 1);
|
ret = chp_process_crw(crw[0].rsid, 1);
|
||||||
break;
|
break;
|
||||||
case CRW_ERC_PERRI: /* Path has gone. */
|
case CRW_ERC_PERRI: /* Path has gone. */
|
||||||
case CRW_ERC_PERRN:
|
case CRW_ERC_PERRN:
|
||||||
ret = chp_process_crw(crw.rsid, 0);
|
ret = chp_process_crw(crw[0].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[0].erc);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
if (ret == -EAGAIN)
|
if (ret == -EAGAIN)
|
||||||
@ -115,6 +141,8 @@ s390_collect_crw_info(void *param)
|
|||||||
pr_debug("unknown source\n");
|
pr_debug("unknown source\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
/* chain is always 0 or 1 here. */
|
||||||
|
chain = crw[chain].chn ? chain + 1 : 0;
|
||||||
}
|
}
|
||||||
if (slow)
|
if (slow)
|
||||||
queue_work(slow_path_wq, &slow_path_work);
|
queue_work(slow_path_wq, &slow_path_work);
|
||||||
|
Loading…
Reference in New Issue
Block a user