|
@@ -26,155 +26,84 @@
|
|
|
|
|
|
static void *sei_page;
|
|
|
|
|
|
-/* FIXME: this is _always_ called for every subchannel. shouldn't we
|
|
|
- * process more than one at a time? */
|
|
|
-static int
|
|
|
-chsc_get_sch_desc_irq(struct subchannel *sch, void *page)
|
|
|
-{
|
|
|
- int ccode, j;
|
|
|
+struct chsc_ssd_area {
|
|
|
+ struct chsc_header request;
|
|
|
+ u16 :10;
|
|
|
+ u16 ssid:2;
|
|
|
+ u16 :4;
|
|
|
+ u16 f_sch; /* first subchannel */
|
|
|
+ u16 :16;
|
|
|
+ u16 l_sch; /* last subchannel */
|
|
|
+ u32 :32;
|
|
|
+ struct chsc_header response;
|
|
|
+ u32 :32;
|
|
|
+ u8 sch_valid : 1;
|
|
|
+ u8 dev_valid : 1;
|
|
|
+ u8 st : 3; /* subchannel type */
|
|
|
+ u8 zeroes : 3;
|
|
|
+ u8 unit_addr; /* unit address */
|
|
|
+ u16 devno; /* device number */
|
|
|
+ u8 path_mask;
|
|
|
+ u8 fla_valid_mask;
|
|
|
+ u16 sch; /* subchannel */
|
|
|
+ u8 chpid[8]; /* chpids 0-7 */
|
|
|
+ u16 fla[8]; /* full link addresses 0-7 */
|
|
|
+} __attribute__ ((packed));
|
|
|
|
|
|
- struct {
|
|
|
- struct chsc_header request;
|
|
|
- u16 reserved1a:10;
|
|
|
- u16 ssid:2;
|
|
|
- u16 reserved1b:4;
|
|
|
- u16 f_sch; /* first subchannel */
|
|
|
- u16 reserved2;
|
|
|
- u16 l_sch; /* last subchannel */
|
|
|
- u32 reserved3;
|
|
|
- struct chsc_header response;
|
|
|
- u32 reserved4;
|
|
|
- u8 sch_valid : 1;
|
|
|
- u8 dev_valid : 1;
|
|
|
- u8 st : 3; /* subchannel type */
|
|
|
- u8 zeroes : 3;
|
|
|
- u8 unit_addr; /* unit address */
|
|
|
- u16 devno; /* device number */
|
|
|
- u8 path_mask;
|
|
|
- u8 fla_valid_mask;
|
|
|
- u16 sch; /* subchannel */
|
|
|
- u8 chpid[8]; /* chpids 0-7 */
|
|
|
- u16 fla[8]; /* full link addresses 0-7 */
|
|
|
- } __attribute__ ((packed)) *ssd_area;
|
|
|
-
|
|
|
- ssd_area = page;
|
|
|
+int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
|
|
|
+{
|
|
|
+ unsigned long page;
|
|
|
+ struct chsc_ssd_area *ssd_area;
|
|
|
+ int ccode;
|
|
|
+ int ret;
|
|
|
+ int i;
|
|
|
+ int mask;
|
|
|
|
|
|
+ page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
|
|
|
+ if (!page)
|
|
|
+ return -ENOMEM;
|
|
|
+ ssd_area = (struct chsc_ssd_area *) page;
|
|
|
ssd_area->request.length = 0x0010;
|
|
|
ssd_area->request.code = 0x0004;
|
|
|
-
|
|
|
- ssd_area->ssid = sch->schid.ssid;
|
|
|
- ssd_area->f_sch = sch->schid.sch_no;
|
|
|
- ssd_area->l_sch = sch->schid.sch_no;
|
|
|
+ ssd_area->ssid = schid.ssid;
|
|
|
+ ssd_area->f_sch = schid.sch_no;
|
|
|
+ ssd_area->l_sch = schid.sch_no;
|
|
|
|
|
|
ccode = chsc(ssd_area);
|
|
|
+ /* Check response. */
|
|
|
if (ccode > 0) {
|
|
|
- pr_debug("chsc returned with ccode = %d\n", ccode);
|
|
|
- return (ccode == 3) ? -ENODEV : -EBUSY;
|
|
|
+ ret = (ccode == 3) ? -ENODEV : -EBUSY;
|
|
|
+ goto out_free;
|
|
|
}
|
|
|
-
|
|
|
- switch (ssd_area->response.code) {
|
|
|
- case 0x0001: /* everything ok */
|
|
|
- break;
|
|
|
- case 0x0002:
|
|
|
- CIO_CRW_EVENT(2, "Invalid command!\n");
|
|
|
- return -EINVAL;
|
|
|
- case 0x0003:
|
|
|
- CIO_CRW_EVENT(2, "Error in chsc request block!\n");
|
|
|
- return -EINVAL;
|
|
|
- case 0x0004:
|
|
|
- CIO_CRW_EVENT(2, "Model does not provide ssd\n");
|
|
|
- return -EOPNOTSUPP;
|
|
|
- default:
|
|
|
- CIO_CRW_EVENT(2, "Unknown CHSC response %d\n",
|
|
|
+ if (ssd_area->response.code != 0x0001) {
|
|
|
+ CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n",
|
|
|
+ schid.ssid, schid.sch_no,
|
|
|
ssd_area->response.code);
|
|
|
- return -EIO;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * ssd_area->st stores the type of the detected
|
|
|
- * subchannel, with the following definitions:
|
|
|
- *
|
|
|
- * 0: I/O subchannel: All fields have meaning
|
|
|
- * 1: CHSC subchannel: Only sch_val, st and sch
|
|
|
- * have meaning
|
|
|
- * 2: Message subchannel: All fields except unit_addr
|
|
|
- * have meaning
|
|
|
- * 3: ADM subchannel: Only sch_val, st and sch
|
|
|
- * have meaning
|
|
|
- *
|
|
|
- * Other types are currently undefined.
|
|
|
- */
|
|
|
- if (ssd_area->st > 3) { /* uhm, that looks strange... */
|
|
|
- CIO_CRW_EVENT(0, "Strange subchannel type %d"
|
|
|
- " 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
|
|
|
- * fields have meaning and what to do with it we just jump out
|
|
|
- */
|
|
|
- return 0;
|
|
|
- } else {
|
|
|
- const char *type[4] = {"I/O", "chsc", "message", "ADM"};
|
|
|
- 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;
|
|
|
+ ret = -EIO;
|
|
|
+ goto out_free;
|
|
|
}
|
|
|
-
|
|
|
- if (ssd_area->st == 0 || ssd_area->st == 2) {
|
|
|
- for (j = 0; j < 8; j++) {
|
|
|
- if (!((0x80 >> j) & ssd_area->path_mask &
|
|
|
- ssd_area->fla_valid_mask))
|
|
|
- continue;
|
|
|
- sch->ssd_info.chpid[j] = ssd_area->chpid[j];
|
|
|
- sch->ssd_info.fla[j] = ssd_area->fla[j];
|
|
|
- }
|
|
|
+ if (!ssd_area->sch_valid) {
|
|
|
+ ret = -ENODEV;
|
|
|
+ goto out_free;
|
|
|
}
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-int
|
|
|
-css_get_ssd_info(struct subchannel *sch)
|
|
|
-{
|
|
|
- int ret;
|
|
|
- void *page;
|
|
|
-
|
|
|
- page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
|
|
|
- if (!page)
|
|
|
- return -ENOMEM;
|
|
|
- spin_lock_irq(sch->lock);
|
|
|
- ret = chsc_get_sch_desc_irq(sch, page);
|
|
|
- if (ret) {
|
|
|
- static int cio_chsc_err_msg;
|
|
|
-
|
|
|
- if (!cio_chsc_err_msg) {
|
|
|
- printk(KERN_ERR
|
|
|
- "chsc_get_sch_descriptions:"
|
|
|
- " Error %d while doing chsc; "
|
|
|
- "processing some machine checks may "
|
|
|
- "not work\n", ret);
|
|
|
- cio_chsc_err_msg = 1;
|
|
|
- }
|
|
|
- }
|
|
|
- spin_unlock_irq(sch->lock);
|
|
|
- free_page((unsigned long)page);
|
|
|
- if (!ret) {
|
|
|
- int j, mask;
|
|
|
- struct chp_id chpid;
|
|
|
-
|
|
|
- chp_id_init(&chpid);
|
|
|
- /* Allocate channel path structures, if needed. */
|
|
|
- for (j = 0; j < 8; j++) {
|
|
|
- mask = 0x80 >> j;
|
|
|
- chpid.id = sch->ssd_info.chpid[j];
|
|
|
- if ((sch->schib.pmcw.pim & mask) &&
|
|
|
- !chp_is_registered(chpid))
|
|
|
- chp_new(chpid);
|
|
|
+ /* Copy data */
|
|
|
+ ret = 0;
|
|
|
+ memset(ssd, 0, sizeof(struct chsc_ssd_info));
|
|
|
+ if ((ssd_area->st != 0) && (ssd_area->st != 2))
|
|
|
+ goto out_free;
|
|
|
+ ssd->path_mask = ssd_area->path_mask;
|
|
|
+ ssd->fla_valid_mask = ssd_area->fla_valid_mask;
|
|
|
+ for (i = 0; i < 8; i++) {
|
|
|
+ mask = 0x80 >> i;
|
|
|
+ if (ssd_area->path_mask & mask) {
|
|
|
+ chp_id_init(&ssd->chpid[i]);
|
|
|
+ ssd->chpid[i].id = ssd_area->chpid[i];
|
|
|
}
|
|
|
+ if (ssd_area->fla_valid_mask & mask)
|
|
|
+ ssd->fla[i] = ssd_area->fla[i];
|
|
|
}
|
|
|
+out_free:
|
|
|
+ free_page(page);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -276,47 +205,6 @@ void chsc_chp_offline(struct chp_id chpid)
|
|
|
s390_subchannel_remove_chpid);
|
|
|
}
|
|
|
|
|
|
-struct res_acc_data {
|
|
|
- struct chp_id chpid;
|
|
|
- u32 fla_mask;
|
|
|
- u16 fla;
|
|
|
-};
|
|
|
-
|
|
|
-static int s390_process_res_acc_sch(struct res_acc_data *res_data,
|
|
|
- struct subchannel *sch)
|
|
|
-{
|
|
|
- int found;
|
|
|
- int chp;
|
|
|
- int ccode;
|
|
|
-
|
|
|
- found = 0;
|
|
|
- for (chp = 0; chp <= 7; chp++)
|
|
|
- /*
|
|
|
- * check if chpid is in information updated by ssd
|
|
|
- */
|
|
|
- if (sch->ssd_info.valid &&
|
|
|
- sch->ssd_info.chpid[chp] == res_data->chpid.id &&
|
|
|
- (sch->ssd_info.fla[chp] & res_data->fla_mask)
|
|
|
- == res_data->fla) {
|
|
|
- found = 1;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- if (found == 0)
|
|
|
- return 0;
|
|
|
-
|
|
|
- /*
|
|
|
- * Do a stsch to update our subchannel structure with the
|
|
|
- * new path information and eventually check for logically
|
|
|
- * offline chpids.
|
|
|
- */
|
|
|
- ccode = stsch(sch->schid, &sch->schib);
|
|
|
- if (ccode > 0)
|
|
|
- return 0;
|
|
|
-
|
|
|
- return 0x80 >> chp;
|
|
|
-}
|
|
|
-
|
|
|
static int
|
|
|
s390_process_res_acc_new_sch(struct subchannel_id schid)
|
|
|
{
|
|
@@ -338,6 +226,32 @@ s390_process_res_acc_new_sch(struct subchannel_id schid)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+struct res_acc_data {
|
|
|
+ struct chp_id chpid;
|
|
|
+ u32 fla_mask;
|
|
|
+ u16 fla;
|
|
|
+};
|
|
|
+
|
|
|
+static int get_res_chpid_mask(struct chsc_ssd_info *ssd,
|
|
|
+ struct res_acc_data *data)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ int mask;
|
|
|
+
|
|
|
+ for (i = 0; i < 8; i++) {
|
|
|
+ mask = 0x80 >> i;
|
|
|
+ if (!(ssd->path_mask & mask))
|
|
|
+ continue;
|
|
|
+ if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid))
|
|
|
+ continue;
|
|
|
+ if ((ssd->fla_valid_mask & mask) &&
|
|
|
+ ((ssd->fla[i] & data->fla_mask) != data->fla))
|
|
|
+ continue;
|
|
|
+ return mask;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
__s390_process_res_acc(struct subchannel_id schid, void *data)
|
|
|
{
|
|
@@ -352,14 +266,11 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
|
|
|
return s390_process_res_acc_new_sch(schid);
|
|
|
|
|
|
spin_lock_irq(sch->lock);
|
|
|
-
|
|
|
- chp_mask = s390_process_res_acc_sch(res_data, sch);
|
|
|
-
|
|
|
- if (chp_mask == 0) {
|
|
|
- spin_unlock_irq(sch->lock);
|
|
|
- put_device(&sch->dev);
|
|
|
- return 0;
|
|
|
- }
|
|
|
+ chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data);
|
|
|
+ if (chp_mask == 0)
|
|
|
+ goto out;
|
|
|
+ if (stsch(sch->schid, &sch->schib))
|
|
|
+ goto out;
|
|
|
old_lpm = sch->lpm;
|
|
|
sch->lpm = ((sch->schib.pmcw.pim &
|
|
|
sch->schib.pmcw.pam &
|
|
@@ -369,13 +280,12 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
|
|
|
device_trigger_reprobe(sch);
|
|
|
else if (sch->driver && sch->driver->verify)
|
|
|
sch->driver->verify(&sch->dev);
|
|
|
-
|
|
|
+out:
|
|
|
spin_unlock_irq(sch->lock);
|
|
|
put_device(&sch->dev);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
static void s390_process_res_acc (struct res_acc_data *res_data)
|
|
|
{
|
|
|
char dbf_txt[15];
|
|
@@ -661,29 +571,30 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch,
|
|
|
struct chp_id chpid, int on)
|
|
|
{
|
|
|
int chp, old_lpm;
|
|
|
+ int mask;
|
|
|
unsigned long flags;
|
|
|
|
|
|
- if (!sch->ssd_info.valid)
|
|
|
- return;
|
|
|
-
|
|
|
spin_lock_irqsave(sch->lock, flags);
|
|
|
old_lpm = sch->lpm;
|
|
|
for (chp = 0; chp < 8; chp++) {
|
|
|
- if (sch->ssd_info.chpid[chp] != chpid.id)
|
|
|
+ mask = 0x80 >> chp;
|
|
|
+ if (!(sch->ssd_info.path_mask & mask))
|
|
|
+ continue;
|
|
|
+ if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid))
|
|
|
continue;
|
|
|
|
|
|
if (on) {
|
|
|
- sch->opm |= (0x80 >> chp);
|
|
|
- sch->lpm |= (0x80 >> chp);
|
|
|
+ sch->opm |= mask;
|
|
|
+ sch->lpm |= mask;
|
|
|
if (!old_lpm)
|
|
|
device_trigger_reprobe(sch);
|
|
|
else if (sch->driver && sch->driver->verify)
|
|
|
sch->driver->verify(&sch->dev);
|
|
|
break;
|
|
|
}
|
|
|
- sch->opm &= ~(0x80 >> chp);
|
|
|
- sch->lpm &= ~(0x80 >> chp);
|
|
|
- if (check_for_io_on_path(sch, (0x80 >> chp))) {
|
|
|
+ sch->opm &= ~mask;
|
|
|
+ sch->lpm &= ~mask;
|
|
|
+ if (check_for_io_on_path(sch, mask)) {
|
|
|
if (device_is_online(sch))
|
|
|
/* Path verification is done after killing. */
|
|
|
device_kill_io(sch);
|