X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fs390%2Fcio%2Fchsc.c;h=6f05a44e38170c9df7d662298256a986d3873fd0;hb=67c2b7d9d224232ee730b9c9444abed824b62e7a;hp=3bb4e472d73de1f03d111467d9efdd9665067718;hpb=3eeab61aa3ddd3c0bedb7449ada1599de22fdb5a;p=linux-2.6 diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 3bb4e472d7..6f05a44e38 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -93,7 +93,7 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page) u16 sch; /* subchannel */ u8 chpid[8]; /* chpids 0-7 */ u16 fla[8]; /* full link addresses 0-7 */ - } *ssd_area; + } __attribute__ ((packed)) *ssd_area; ssd_area = page; @@ -183,7 +183,7 @@ css_get_ssd_info(struct subchannel *sch) page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!page) return -ENOMEM; - spin_lock_irq(&sch->lock); + spin_lock_irq(sch->lock); ret = chsc_get_sch_desc_irq(sch, page); if (ret) { static int cio_chsc_err_msg; @@ -197,14 +197,16 @@ css_get_ssd_info(struct subchannel *sch) cio_chsc_err_msg = 1; } } - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); free_page((unsigned long)page); if (!ret) { - int j, chpid; + int j, chpid, mask; /* Allocate channel path structures, if needed. */ for (j = 0; j < 8; j++) { + mask = 0x80 >> j; chpid = sch->ssd_info.chpid[j]; - if (chpid && (get_chp_status(chpid) < 0)) + if ((sch->schib.pmcw.pim & mask) && + (get_chp_status(chpid) < 0)) new_channel_path(chpid); } } @@ -222,14 +224,16 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) sch = to_subchannel(dev); chpid = data; - for (j = 0; j < 8; j++) - if (sch->schib.pmcw.chpid[j] == chpid->id) + for (j = 0; j < 8; j++) { + mask = 0x80 >> j; + if ((sch->schib.pmcw.pim & mask) && + (sch->schib.pmcw.chpid[j] == chpid->id)) break; + } if (j >= 8) return 0; - mask = 0x80 >> j; - spin_lock_irq(&sch->lock); + spin_lock_irq(sch->lock); stsch(sch->schid, &schib); if (!schib.pmcw.dnv) @@ -247,6 +251,8 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) cc = cio_clear(sch); if (cc == -ENODEV) goto out_unreg; + /* Request retry of internal operation. */ + device_set_intretry(sch); /* Call handler. */ if (sch->driver && sch->driver->termination) sch->driver->termination(&sch->dev); @@ -259,10 +265,10 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) else if (sch->lpm == mask) goto out_unreg; out_unlock: - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); return 0; out_unreg: - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); sch->lpm = 0; if (css_enqueue_subchannel_slow(sch->schid)) { css_clear_subchannel_slow_list(); @@ -271,7 +277,7 @@ out_unreg: return 0; } -static inline void +static void s390_set_chpid_offline( __u8 chpid) { char dbf_txt[15]; @@ -332,7 +338,7 @@ s390_process_res_acc_sch(struct res_acc_data *res_data, struct subchannel *sch) return 0x80 >> chp; } -static inline int +static int s390_process_res_acc_new_sch(struct subchannel_id schid) { struct schib schib; @@ -366,18 +372,18 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) struct res_acc_data *res_data; struct subchannel *sch; - res_data = (struct res_acc_data *)data; + res_data = data; sch = get_subchannel_by_schid(schid); if (!sch) /* Check if a subchannel is newly available. */ return s390_process_res_acc_new_sch(schid); - spin_lock_irq(&sch->lock); + spin_lock_irq(sch->lock); chp_mask = s390_process_res_acc_sch(res_data, sch); if (chp_mask == 0) { - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); put_device(&sch->dev); return 0; } @@ -391,7 +397,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) else if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); put_device(&sch->dev); return 0; } @@ -438,9 +444,9 @@ __get_chpid_from_lir(void *data) u32 andesc[28]; /* incident-specific information */ u32 isinfo[28]; - } *lir; + } __attribute__ ((packed)) *lir; - lir = (struct lir*) data; + lir = data; if (!(lir->iq&0x80)) /* NULL link incident record */ return -EINVAL; @@ -455,154 +461,146 @@ __get_chpid_from_lir(void *data) return (u16) (lir->indesc[0]&0x000000ff); } -int -chsc_process_crw(void) +struct chsc_sei_area { + struct chsc_header request; + u32 reserved1; + u32 reserved2; + u32 reserved3; + struct chsc_header response; + u32 reserved4; + u8 flags; + u8 vf; /* validity flags */ + u8 rs; /* reporting source */ + u8 cc; /* content code */ + u16 fla; /* full link address */ + u16 rsid; /* reporting source id */ + u32 reserved5; + u32 reserved6; + u8 ccdf[4096 - 16 - 24]; /* content-code dependent field */ + /* ccdf has to be big enough for a link-incident record */ +} __attribute__ ((packed)); + +static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) +{ + int chpid; + + CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n", + sei_area->rs, sei_area->rsid); + if (sei_area->rs != 4) + return 0; + chpid = __get_chpid_from_lir(sei_area->ccdf); + if (chpid < 0) + CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n"); + else + s390_set_chpid_offline(chpid); + + return 0; +} + +static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) { - int chpid, ret; struct res_acc_data res_data; - struct { - struct chsc_header request; - u32 reserved1; - u32 reserved2; - u32 reserved3; - struct chsc_header response; - u32 reserved4; - u8 flags; - u8 vf; /* validity flags */ - u8 rs; /* reporting source */ - u8 cc; /* content code */ - u16 fla; /* full link address */ - u16 rsid; /* reporting source id */ - u32 reserved5; - u32 reserved6; - u32 ccdf[96]; /* content-code dependent field */ - /* ccdf has to be big enough for a link-incident record */ - } *sei_area; + struct device *dev; + int status; + int rc; + + CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, " + "rs_id=%04x)\n", sei_area->rs, sei_area->rsid); + if (sei_area->rs != 4) + return 0; + /* allocate a new channel path structure, if needed */ + status = get_chp_status(sei_area->rsid); + if (status < 0) + new_channel_path(sei_area->rsid); + else if (!status) + return 0; + dev = get_device(&css[0]->chps[sei_area->rsid]->dev); + memset(&res_data, 0, sizeof(struct res_acc_data)); + res_data.chp = to_channelpath(dev); + if ((sei_area->vf & 0xc0) != 0) { + res_data.fla = sei_area->fla; + if ((sei_area->vf & 0xc0) == 0xc0) + /* full link address */ + res_data.fla_mask = 0xffff; + else + /* link address */ + res_data.fla_mask = 0xff00; + } + rc = s390_process_res_acc(&res_data); + put_device(dev); + + return rc; +} + +static int chsc_process_sei(struct chsc_sei_area *sei_area) +{ + int rc; + + /* Check if we might have lost some information. */ + if (sei_area->flags & 0x40) + CIO_CRW_EVENT(2, "chsc: event overflow\n"); + /* which kind of information was stored? */ + rc = 0; + switch (sei_area->cc) { + case 1: /* link incident*/ + rc = chsc_process_sei_link_incident(sei_area); + break; + case 2: /* i/o resource accessibiliy */ + rc = chsc_process_sei_res_acc(sei_area); + break; + default: /* other stuff */ + CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n", + sei_area->cc); + break; + } + + return rc; +} + +int chsc_process_crw(void) +{ + struct chsc_sei_area *sei_area; + int ret; + int rc; if (!sei_page) return 0; - /* - * build the chsc request block for store event information - * and do the call - * This function is only called by the machine check handler thread, - * so we don't need locking for the sei_page. - */ + /* Access to sei_page is serialized through machine check handler + * thread, so no need for locking. */ sei_area = sei_page; CIO_TRACE_EVENT( 2, "prcss"); ret = 0; do { - int ccode, status; - struct device *dev; memset(sei_area, 0, sizeof(*sei_area)); - memset(&res_data, 0, sizeof(struct res_acc_data)); sei_area->request.length = 0x0010; sei_area->request.code = 0x000e; + if (chsc(sei_area)) + break; - ccode = chsc(sei_area); - if (ccode > 0) - return 0; - - switch (sei_area->response.code) { - /* for debug purposes, check for problems */ - case 0x0001: - CIO_CRW_EVENT(4, "chsc_process_crw: event information " - "successfully stored\n"); - break; /* everything ok */ - case 0x0002: - CIO_CRW_EVENT(2, - "chsc_process_crw: invalid command!\n"); - return 0; - case 0x0003: - CIO_CRW_EVENT(2, "chsc_process_crw: error in chsc " - "request block!\n"); - return 0; - case 0x0005: - CIO_CRW_EVENT(2, "chsc_process_crw: no event " - "information stored\n"); - return 0; - default: - CIO_CRW_EVENT(2, "chsc_process_crw: chsc response %d\n", + if (sei_area->response.code == 0x0001) { + CIO_CRW_EVENT(4, "chsc: sei successful\n"); + rc = chsc_process_sei(sei_area); + if (rc) + ret = rc; + } else { + CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", sei_area->response.code); - return 0; - } - - /* Check if we might have lost some information. */ - if (sei_area->flags & 0x40) - CIO_CRW_EVENT(2, "chsc_process_crw: Event information " - "has been lost due to overflow!\n"); - - if (sei_area->rs != 4) { - CIO_CRW_EVENT(2, "chsc_process_crw: reporting source " - "(%04X) isn't a chpid!\n", - sei_area->rsid); - continue; - } - - /* which kind of information was stored? */ - switch (sei_area->cc) { - case 1: /* link incident*/ - CIO_CRW_EVENT(4, "chsc_process_crw: " - "channel subsystem reports link incident," - " reporting source is chpid %x\n", - sei_area->rsid); - chpid = __get_chpid_from_lir(sei_area->ccdf); - if (chpid < 0) - CIO_CRW_EVENT(4, "%s: Invalid LIR, skipping\n", - __FUNCTION__); - else - s390_set_chpid_offline(chpid); - break; - - case 2: /* i/o resource accessibiliy */ - CIO_CRW_EVENT(4, "chsc_process_crw: " - "channel subsystem reports some I/O " - "devices may have become accessible\n"); - pr_debug("Data received after sei: \n"); - pr_debug("Validity flags: %x\n", sei_area->vf); - - /* allocate a new channel path structure, if needed */ - status = get_chp_status(sei_area->rsid); - if (status < 0) - new_channel_path(sei_area->rsid); - else if (!status) - break; - dev = get_device(&css[0]->chps[sei_area->rsid]->dev); - res_data.chp = to_channelpath(dev); - pr_debug("chpid: %x", sei_area->rsid); - if ((sei_area->vf & 0xc0) != 0) { - res_data.fla = sei_area->fla; - if ((sei_area->vf & 0xc0) == 0xc0) { - pr_debug(" full link addr: %x", - sei_area->fla); - res_data.fla_mask = 0xffff; - } else { - pr_debug(" link addr: %x", - sei_area->fla); - res_data.fla_mask = 0xff00; - } - } - ret = s390_process_res_acc(&res_data); - pr_debug("\n\n"); - put_device(dev); - break; - - default: /* other stuff */ - CIO_CRW_EVENT(4, "chsc_process_crw: event %d\n", - sei_area->cc); + ret = 0; break; } } while (sei_area->flags & 0x80); + return ret; } -static inline int +static int __chp_add_new_sch(struct subchannel_id schid) { struct schib schib; int ret; - if (stsch(schid, &schib)) + if (stsch_err(schid, &schib)) /* We're through */ return need_rescan ? -EAGAIN : -ENXIO; @@ -620,38 +618,41 @@ __chp_add_new_sch(struct subchannel_id schid) static int __chp_add(struct subchannel_id schid, void *data) { - int i; + int i, mask; struct channel_path *chp; struct subchannel *sch; - chp = (struct channel_path *)data; + chp = data; sch = get_subchannel_by_schid(schid); if (!sch) /* Check if the subchannel is now available. */ return __chp_add_new_sch(schid); - spin_lock_irq(&sch->lock); - for (i=0; i<8; i++) - if (sch->schib.pmcw.chpid[i] == chp->id) { + spin_lock_irq(sch->lock); + for (i=0; i<8; i++) { + mask = 0x80 >> i; + if ((sch->schib.pmcw.pim & mask) && + (sch->schib.pmcw.chpid[i] == chp->id)) { if (stsch(sch->schid, &sch->schib) != 0) { /* Endgame. */ - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); return -ENXIO; } break; } + } if (i==8) { - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); return 0; } sch->lpm = ((sch->schib.pmcw.pim & sch->schib.pmcw.pam & sch->schib.pmcw.pom) - | 0x80 >> i) & sch->opm; + | mask) & sch->opm; if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); - spin_unlock_irq(&sch->lock); + spin_unlock_irq(sch->lock); put_device(&sch->dev); return 0; } @@ -700,25 +701,39 @@ chp_process_crw(int chpid, int on) return chp_add(chpid); } -static inline int -__check_for_io_and_kill(struct subchannel *sch, int index) +static int check_for_io_on_path(struct subchannel *sch, int index) { int cc; - if (!device_is_online(sch)) - /* cio could be doing I/O. */ - return 0; cc = stsch(sch->schid, &sch->schib); if (cc) return 0; - if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == (0x80 >> index)) { - device_set_waiting(sch); + if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == (0x80 >> index)) return 1; - } return 0; } -static inline void +static void terminate_internal_io(struct subchannel *sch) +{ + if (cio_clear(sch)) { + /* Recheck device in case clear failed. */ + sch->lpm = 0; + if (device_trigger_verify(sch) != 0) { + if(css_enqueue_subchannel_slow(sch->schid)) { + css_clear_subchannel_slow_list(); + need_rescan = 1; + } + } + return; + } + /* Request retry of internal operation. */ + device_set_intretry(sch); + /* Call handler. */ + if (sch->driver && sch->driver->termination) + sch->driver->termination(&sch->dev); +} + +static void __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) { int chp, old_lpm; @@ -727,7 +742,7 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) if (!sch->ssd_info.valid) return; - spin_lock_irqsave(&sch->lock, flags); + spin_lock_irqsave(sch->lock, flags); old_lpm = sch->lpm; for (chp = 0; chp < 8; chp++) { if (sch->ssd_info.chpid[chp] != chpid) @@ -740,25 +755,29 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) device_trigger_reprobe(sch); else if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); - } else { - sch->opm &= ~(0x80 >> chp); - sch->lpm &= ~(0x80 >> chp); - /* - * Give running I/O a grace period in which it - * can successfully terminate, even using the - * just varied off path. Then kill it. - */ - if (!__check_for_io_and_kill(sch, chp) && !sch->lpm) { + break; + } + sch->opm &= ~(0x80 >> chp); + sch->lpm &= ~(0x80 >> chp); + if (check_for_io_on_path(sch, chp)) { + if (device_is_online(sch)) + /* Path verification is done after killing. */ + device_kill_io(sch); + else + /* Kill and retry internal I/O. */ + terminate_internal_io(sch); + } else if (!sch->lpm) { + if (device_trigger_verify(sch) != 0) { if (css_enqueue_subchannel_slow(sch->schid)) { css_clear_subchannel_slow_list(); need_rescan = 1; } - } else if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); - } + } + } else if (sch->driver && sch->driver->verify) + sch->driver->verify(&sch->dev); break; } - spin_unlock_irqrestore(&sch->lock, flags); + spin_unlock_irqrestore(sch->lock, flags); } static int @@ -940,8 +959,8 @@ static struct bin_attribute chp_measurement_attr = { static void chsc_remove_chp_cmg_attr(struct channel_path *chp) { - sysfs_remove_bin_file(&chp->dev.kobj, &chp_measurement_chars_attr); - sysfs_remove_bin_file(&chp->dev.kobj, &chp_measurement_attr); + device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); + device_remove_bin_file(&chp->dev, &chp_measurement_attr); } static int @@ -949,14 +968,12 @@ chsc_add_chp_cmg_attr(struct channel_path *chp) { int ret; - ret = sysfs_create_bin_file(&chp->dev.kobj, - &chp_measurement_chars_attr); + ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr); if (ret) return ret; - ret = sysfs_create_bin_file(&chp->dev.kobj, &chp_measurement_attr); + ret = device_create_bin_file(&chp->dev, &chp_measurement_attr); if (ret) - sysfs_remove_bin_file(&chp->dev.kobj, - &chp_measurement_chars_attr); + device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); return ret; } @@ -1015,7 +1032,7 @@ __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) u32 : 4; u32 fmt : 4; u32 : 16; - } *secm_area; + } __attribute__ ((packed)) *secm_area; int ret, ccode; secm_area = page; @@ -1226,7 +1243,7 @@ chsc_determine_channel_path_description(int chpid, struct chsc_header response; u32 zeroes2; struct channel_path_desc desc; - } *scpd_area; + } __attribute__ ((packed)) *scpd_area; scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!scpd_area) @@ -1323,7 +1340,7 @@ chsc_get_channel_measurement_chars(struct channel_path *chp) u32 cmg : 8; u32 zeroes3; u32 data[NR_MEASUREMENT_CHARS]; - } *scmc_area; + } __attribute__ ((packed)) *scmc_area; scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!scmc_area) @@ -1463,41 +1480,6 @@ chsc_get_chp_desc(struct subchannel *sch, int chp_no) return desc; } -static int reset_channel_path(struct channel_path *chp) -{ - int cc; - - cc = rchp(chp->id); - switch (cc) { - case 0: - return 0; - case 2: - return -EBUSY; - default: - return -ENODEV; - } -} - -static void reset_channel_paths_css(struct channel_subsystem *css) -{ - int i; - - for (i = 0; i <= __MAX_CHPID; i++) { - if (css->chps[i]) - reset_channel_path(css->chps[i]); - } -} - -void cio_reset_channel_paths(void) -{ - int i; - - for (i = 0; i <= __MAX_CSSID; i++) { - if (css[i] && css[i]->valid) - reset_channel_paths_css(css[i]); - } -} - static int __init chsc_alloc_sei_area(void) { @@ -1525,7 +1507,7 @@ chsc_enable_facility(int operation_code) u32 reserved5:4; u32 format2:4; u32 reserved6:24; - } *sda_area; + } __attribute__ ((packed)) *sda_area; sda_area = (void *)get_zeroed_page(GFP_KERNEL|GFP_DMA); if (!sda_area) @@ -1577,7 +1559,7 @@ chsc_determine_css_characteristics(void) u32 reserved4; u32 general_char[510]; u32 chsc_char[518]; - } *scsc_area; + } __attribute__ ((packed)) *scsc_area; scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!scsc_area) {