📄 chsc.c
字号:
/* 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); break; } } while (sei_area->flags & 0x80); return ret;}static inline int__chp_add_new_sch(struct subchannel_id schid){ struct schib schib; int ret; if (stsch(schid, &schib)) /* We're through */ return need_rescan ? -EAGAIN : -ENXIO; /* Put it on the slow path. */ ret = css_enqueue_subchannel_slow(schid); if (ret) { css_clear_subchannel_slow_list(); need_rescan = 1; return -EAGAIN; } return 0;}static int__chp_add(struct subchannel_id schid, void *data){ int i; struct channel_path *chp; struct subchannel *sch; chp = (struct channel_path *)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) { if (stsch(sch->schid, &sch->schib) != 0) { /* Endgame. */ spin_unlock_irq(&sch->lock); return -ENXIO; } break; } if (i==8) { 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; if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); spin_unlock_irq(&sch->lock); put_device(&sch->dev); return 0;}static intchp_add(int chpid){ int rc; char dbf_txt[15]; struct device *dev; if (!get_chp_status(chpid)) return 0; /* no need to do the rest */ sprintf(dbf_txt, "cadd%x", chpid); CIO_TRACE_EVENT(2, dbf_txt); dev = get_device(&css[0]->chps[chpid]->dev); rc = for_each_subchannel(__chp_add, to_channelpath(dev)); if (css_slow_subchannels_exist()) rc = -EAGAIN; if (rc != -EAGAIN) rc = 0; put_device(dev); return rc;}/* * Handling of crw machine checks with channel path source. */intchp_process_crw(int chpid, int on){ if (on == 0) { /* Path has gone. We use the link incident routine.*/ s390_set_chpid_offline(chpid); return 0; /* De-register is async anyway. */ } /* * Path has come. Allocate a new channel path structure, * if needed. */ if (get_chp_status(chpid) < 0) new_channel_path(chpid); /* Avoid the extra overhead in process_rec_acc. */ return chp_add(chpid);}static inline int__check_for_io_and_kill(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); return 1; } return 0;}static inline void__s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on){ int chp, old_lpm; 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) continue; if (on) { sch->opm |= (0x80 >> chp); sch->lpm |= (0x80 >> chp); if (!old_lpm) 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) { 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); } break; } spin_unlock_irqrestore(&sch->lock, flags);}static ints390_subchannel_vary_chpid_off(struct device *dev, void *data){ struct subchannel *sch; __u8 *chpid; sch = to_subchannel(dev); chpid = data; __s390_subchannel_vary_chpid(sch, *chpid, 0); return 0;}static ints390_subchannel_vary_chpid_on(struct device *dev, void *data){ struct subchannel *sch; __u8 *chpid; sch = to_subchannel(dev); chpid = data; __s390_subchannel_vary_chpid(sch, *chpid, 1); return 0;}static int__s390_vary_chpid_on(struct subchannel_id schid, void *data){ struct schib schib; struct subchannel *sch; sch = get_subchannel_by_schid(schid); if (sch) { put_device(&sch->dev); return 0; } if (stsch_err(schid, &schib)) /* We're through */ return -ENXIO; /* Put it on the slow path. */ if (css_enqueue_subchannel_slow(schid)) { css_clear_subchannel_slow_list(); need_rescan = 1; return -EAGAIN; } return 0;}/* * Function: s390_vary_chpid * Varies the specified chpid online or offline */static ints390_vary_chpid( __u8 chpid, int on){ char dbf_text[15]; int status; sprintf(dbf_text, on?"varyon%x":"varyoff%x", chpid); CIO_TRACE_EVENT( 2, dbf_text); status = get_chp_status(chpid); if (status < 0) { printk(KERN_ERR "Can't vary unknown chpid %02X\n", chpid); return -EINVAL; } if (!on && !status) { printk(KERN_ERR "chpid %x is already offline\n", chpid); return -EINVAL; } set_chp_logically_online(chpid, on); /* * Redo PathVerification on the devices the chpid connects to */ bus_for_each_dev(&css_bus_type, NULL, &chpid, on ? s390_subchannel_vary_chpid_on : s390_subchannel_vary_chpid_off); if (on) /* Scan for new devices on varied on path. */ for_each_subchannel(__s390_vary_chpid_on, NULL); if (need_rescan || css_slow_subchannels_exist()) queue_work(slow_path_wq, &slow_path_work); return 0;}/* * Channel measurement related functions */static ssize_tchp_measurement_chars_read(struct kobject *kobj, char *buf, loff_t off, size_t count){ struct channel_path *chp; unsigned int size; chp = to_channelpath(container_of(kobj, struct device, kobj)); if (!chp->cmg_chars) return 0; size = sizeof(struct cmg_chars); if (off > size) return 0; if (off + count > size) count = size - off; memcpy(buf, chp->cmg_chars + off, count); return count;}static struct bin_attribute chp_measurement_chars_attr = { .attr = { .name = "measurement_chars", .mode = S_IRUSR, .owner = THIS_MODULE, }, .size = sizeof(struct cmg_chars), .read = chp_measurement_chars_read,};static voidchp_measurement_copy_block(struct cmg_entry *buf, struct channel_subsystem *css, int chpid){ void *area; struct cmg_entry *entry, reference_buf; int idx; if (chpid < 128) { area = css->cub_addr1; idx = chpid; } else { area = css->cub_addr2; idx = chpid - 128; } entry = area + (idx * sizeof(struct cmg_entry)); do { memcpy(buf, entry, sizeof(*entry)); memcpy(&reference_buf, entry, sizeof(*entry)); } while (reference_buf.values[0] != buf->values[0]);}static ssize_tchp_measurement_read(struct kobject *kobj, char *buf, loff_t off, size_t count){ struct channel_path *chp; struct channel_subsystem *css; unsigned int size; chp = to_channelpath(container_of(kobj, struct device, kobj)); css = to_css(chp->dev.parent); size = sizeof(struct cmg_chars); /* Only allow single reads. */ if (off || count < size) return 0; chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->id); return count;}static struct bin_attribute chp_measurement_attr = { .attr = { .name = "measurement", .mode = S_IRUSR, .owner = THIS_MODULE, }, .size = sizeof(struct cmg_entry), .read = chp_measurement_read,};static voidchsc_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);}static intchsc_add_chp_cmg_attr(struct channel_path *chp){ int ret; ret = sysfs_create_bin_file(&chp->dev.kobj, &chp_measurement_chars_attr); if (ret) return ret; ret = sysfs_create_bin_file(&chp->dev.kobj, &chp_measurement_attr); if (ret) sysfs_remove_bin_file(&chp->dev.kobj, &chp_measurement_chars_attr); return ret;}static voidchsc_remove_cmg_attr(struct channel_subsystem *css){ int i; for (i = 0; i <= __MAX_CHPID; i++) { if (!css->chps[i]) continue; chsc_remove_chp_cmg_attr(css->chps[i]); }}static intchsc_add_cmg_attr(struct channel_subsystem *css){ int i, ret; ret = 0; for (i = 0; i <= __MAX_CHPID; i++) { if (!css->chps[i]) continue; ret = chsc_add_chp_cmg_attr(css->chps[i]); if (ret) goto cleanup; } return ret;cleanup: for (--i; i >= 0; i--) { if (!css->chps[i]) continue; chsc_remove_chp_cmg_attr(css->chps[i]); } return ret;}static int__chsc_do_secm(struct channel_subsystem *css, int enable, void *page){ struct { struct chsc_header request; u32 operation_code : 2; u32 : 30; u32 key : 4; u32 : 28; u32 zeroes1; u32 cub_addr1; u32 zeroes2; u32 cub_addr2; u32 reserved[13]; struct chsc_header response; u32 status : 8; u32 : 4; u32 fmt : 4; u32 : 16; } *secm_area; int ret, ccode; secm_area = page; secm_area->request.length = 0x0050; secm_area->request.code = 0x0016; secm_area->key = PAGE_DEFAULT_KEY; secm_area->cub_addr1 = (u64)(unsigned long)css->cub_addr1; secm_area->cub_addr2 = (u64)(unsigned long)css->cub_addr2; secm_area->operation_code = enable ? 0 : 1; ccode = chsc(secm_area); if (ccode > 0) return (ccode == 3) ? -ENODEV : -EBUSY; switch (secm_area->response.code) { case 0x0001: /* Success. */ ret = 0; break; case 0x0003: /* Invalid block. */ case 0x0007: /* Invalid format. */ case 0x0008: /* Other invalid block. */ CIO_CRW_EVENT(2, "Error in chsc request block!\n"); ret = -EINVAL; break; case 0x0004: /* Command not provided in model. */ CIO_CRW_EVENT(2, "Model does not provide secm\n"); ret = -EOPNOTSUPP; break; case 0x0102: /* cub adresses incorrect */ CIO_CRW_EVENT(2, "Invalid addresses in chsc request block\n"); ret = -EINVAL; break; case 0x0103: /* key error */ CIO_CRW_EVENT(2, "Access key error in secm\n"); ret = -EINVAL; break; case 0x0105: /* error while starting */ CIO_CRW_EVENT(2, "Error while starting channel measurement\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -