X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fs390%2Fcio%2Fchsc.c;fp=drivers%2Fs390%2Fcio%2Fchsc.c;h=cbab8d2ce5cfd664b82fd6f189a0fbc9dfbbfb38;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=f4183d660258108728a626b73c22798ba4699570;hpb=76828883507a47dae78837ab5dec5a5b4513c667;p=linux-2.6.git diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index f4183d660..cbab8d2ce 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -10,7 +10,6 @@ */ #include -#include #include #include #include @@ -98,10 +97,8 @@ chsc_get_sch_desc_irq(struct subchannel *sch, void *page) ssd_area = page; - ssd_area->request = (struct chsc_header) { - .length = 0x0010, - .code = 0x0004, - }; + ssd_area->request.length = 0x0010; + ssd_area->request.code = 0x0004; ssd_area->ssid = sch->schid.ssid; ssd_area->f_sch = sch->schid.sch_no; @@ -186,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; @@ -200,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); } } @@ -225,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) @@ -241,36 +242,17 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) /* Check for single path devices. */ if (sch->schib.pmcw.pim == 0x80) goto out_unreg; - if (sch->vpm == mask) - goto out_unreg; - if ((sch->schib.scsw.actl & (SCSW_ACTL_CLEAR_PEND | - SCSW_ACTL_HALT_PEND | - SCSW_ACTL_START_PEND | - SCSW_ACTL_RESUME_PEND)) && + if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) && + (sch->schib.scsw.actl & SCSW_ACTL_SCHACT) && (sch->schib.pmcw.lpum == mask)) { - int cc = cio_cancel(sch); - - if (cc == -ENODEV) - goto out_unreg; - - if (cc == -EINVAL) { - cc = cio_clear(sch); - if (cc == -ENODEV) - goto out_unreg; - /* Call handler. */ - if (sch->driver && sch->driver->termination) - sch->driver->termination(&sch->dev); - goto out_unlock; - } - } else if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) && - (sch->schib.scsw.actl & SCSW_ACTL_SCHACT) && - (sch->schib.pmcw.lpum == mask)) { int cc; 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); @@ -280,11 +262,13 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) /* trigger path verification. */ if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); + 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(); @@ -388,18 +372,19 @@ __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; } old_lpm = sch->lpm; @@ -412,9 +397,9 @@ __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 (res_data->fla_mask == 0xffff) ? -ENODEV : 0; + return 0; } @@ -461,7 +446,7 @@ __get_chpid_from_lir(void *data) u32 isinfo[28]; } *lir; - lir = (struct lir*) data; + lir = data; if (!(lir->iq&0x80)) /* NULL link incident record */ return -EINVAL; @@ -517,10 +502,8 @@ chsc_process_crw(void) struct device *dev; memset(sei_area, 0, sizeof(*sei_area)); memset(&res_data, 0, sizeof(struct res_acc_data)); - sei_area->request = (struct chsc_header) { - .length = 0x0010, - .code = 0x000e, - }; + sei_area->request.length = 0x0010; + sei_area->request.code = 0x000e; ccode = chsc(sei_area); if (ccode > 0) @@ -643,38 +626,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(&sch->lock); + spin_unlock_irq(sch->lock); return -ENXIO; } break; } + } if (i==8) { - spin_unlock(&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; } @@ -723,24 +709,38 @@ 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 inline 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 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 inline void __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) { @@ -750,7 +750,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) @@ -763,25 +763,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 @@ -874,6 +878,265 @@ s390_vary_chpid( __u8 chpid, int on) return 0; } +/* + * Channel measurement related functions + */ +static ssize_t +chp_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 void +chp_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_t +chp_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_entry); + + /* Only allow single reads. */ + if (off || count < size) + return 0; + chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->id); + count = size; + 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 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); +} + +static int +chsc_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 void +chsc_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 int +chsc_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"); + ret = -EIO; + break; + default: + CIO_CRW_EVENT(2, "Unknown CHSC response %d\n", + secm_area->response.code); + ret = -EIO; + } + return ret; +} + +int +chsc_secm(struct channel_subsystem *css, int enable) +{ + void *secm_area; + int ret; + + secm_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!secm_area) + return -ENOMEM; + + mutex_lock(&css->mutex); + if (enable && !css->cm_enabled) { + css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!css->cub_addr1 || !css->cub_addr2) { + free_page((unsigned long)css->cub_addr1); + free_page((unsigned long)css->cub_addr2); + free_page((unsigned long)secm_area); + mutex_unlock(&css->mutex); + return -ENOMEM; + } + } + ret = __chsc_do_secm(css, enable, secm_area); + if (!ret) { + css->cm_enabled = enable; + if (css->cm_enabled) { + ret = chsc_add_cmg_attr(css); + if (ret) { + memset(secm_area, 0, PAGE_SIZE); + __chsc_do_secm(css, 0, secm_area); + css->cm_enabled = 0; + } + } else + chsc_remove_cmg_attr(css); + } + if (enable && !css->cm_enabled) { + free_page((unsigned long)css->cub_addr1); + free_page((unsigned long)css->cub_addr2); + } + mutex_unlock(&css->mutex); + free_page((unsigned long)secm_area); + return ret; +} + /* * Files for the channel path entries. */ @@ -925,9 +1188,39 @@ chp_type_show(struct device *dev, struct device_attribute *attr, char *buf) static DEVICE_ATTR(type, 0444, chp_type_show, NULL); +static ssize_t +chp_cmg_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct channel_path *chp = to_channelpath(dev); + + if (!chp) + return 0; + if (chp->cmg == -1) /* channel measurements not available */ + return sprintf(buf, "unknown\n"); + return sprintf(buf, "%x\n", chp->cmg); +} + +static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL); + +static ssize_t +chp_shared_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct channel_path *chp = to_channelpath(dev); + + if (!chp) + return 0; + if (chp->shared == -1) /* channel measurements not available */ + return sprintf(buf, "unknown\n"); + return sprintf(buf, "%x\n", chp->shared); +} + +static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL); + static struct attribute * chp_attrs[] = { &dev_attr_status.attr, &dev_attr_type.attr, + &dev_attr_cmg.attr, + &dev_attr_shared.attr, NULL, }; @@ -966,10 +1259,8 @@ chsc_determine_channel_path_description(int chpid, if (!scpd_area) return -ENOMEM; - scpd_area->request = (struct chsc_header) { - .length = 0x0010, - .code = 0x0002, - }; + scpd_area->request.length = 0x0010; + scpd_area->request.code = 0x0002; scpd_area->first_chpid = chpid; scpd_area->last_chpid = chpid; @@ -1006,6 +1297,111 @@ out: return ret; } +static void +chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, + struct cmg_chars *chars) +{ + switch (chp->cmg) { + case 2: + case 3: + chp->cmg_chars = kmalloc(sizeof(struct cmg_chars), + GFP_KERNEL); + if (chp->cmg_chars) { + int i, mask; + struct cmg_chars *cmg_chars; + + cmg_chars = chp->cmg_chars; + for (i = 0; i < NR_MEASUREMENT_CHARS; i++) { + mask = 0x80 >> (i + 3); + if (cmcv & mask) + cmg_chars->values[i] = chars->values[i]; + else + cmg_chars->values[i] = 0; + } + } + break; + default: + /* No cmg-dependent data. */ + break; + } +} + +static int +chsc_get_channel_measurement_chars(struct channel_path *chp) +{ + int ccode, ret; + + struct { + struct chsc_header request; + u32 : 24; + u32 first_chpid : 8; + u32 : 24; + u32 last_chpid : 8; + u32 zeroes1; + struct chsc_header response; + u32 zeroes2; + u32 not_valid : 1; + u32 shared : 1; + u32 : 22; + u32 chpid : 8; + u32 cmcv : 5; + u32 : 11; + u32 cmgq : 8; + u32 cmg : 8; + u32 zeroes3; + u32 data[NR_MEASUREMENT_CHARS]; + } *scmc_area; + + scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!scmc_area) + return -ENOMEM; + + scmc_area->request.length = 0x0010; + scmc_area->request.code = 0x0022; + + scmc_area->first_chpid = chp->id; + scmc_area->last_chpid = chp->id; + + ccode = chsc(scmc_area); + if (ccode > 0) { + ret = (ccode == 3) ? -ENODEV : -EBUSY; + goto out; + } + + switch (scmc_area->response.code) { + case 0x0001: /* Success. */ + if (!scmc_area->not_valid) { + chp->cmg = scmc_area->cmg; + chp->shared = scmc_area->shared; + chsc_initialize_cmg_chars(chp, scmc_area->cmcv, + (struct cmg_chars *) + &scmc_area->data); + } else { + chp->cmg = -1; + chp->shared = -1; + } + ret = 0; + break; + case 0x0003: /* Invalid block. */ + case 0x0007: /* Invalid format. */ + case 0x0008: /* Invalid bit combination. */ + CIO_CRW_EVENT(2, "Error in chsc request block!\n"); + ret = -EINVAL; + break; + case 0x0004: /* Command not provided. */ + CIO_CRW_EVENT(2, "Model does not provide scmc\n"); + ret = -EOPNOTSUPP; + break; + default: + CIO_CRW_EVENT(2, "Unknown CHSC response %d\n", + scmc_area->response.code); + ret = -EIO; + } +out: + free_page((unsigned long)scmc_area); + return ret; +} + /* * Entries for chpids on the system bus. * This replaces /proc/chpids. @@ -1016,24 +1412,37 @@ new_channel_path(int chpid) struct channel_path *chp; int ret; - chp = kmalloc(sizeof(struct channel_path), GFP_KERNEL); + chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL); if (!chp) return -ENOMEM; - memset(chp, 0, sizeof(struct channel_path)); /* fill in status, etc. */ chp->id = chpid; chp->state = 1; - chp->dev = (struct device) { - .parent = &css[0]->device, - .release = chp_release, - }; + chp->dev.parent = &css[0]->device; + chp->dev.release = chp_release; snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp0.%x", chpid); /* Obtain channel path description and fill it in. */ ret = chsc_determine_channel_path_description(chpid, &chp->desc); if (ret) goto out_free; + /* Get channel-measurement characteristics. */ + if (css_characteristics_avail && css_chsc_characteristics.scmc + && css_chsc_characteristics.secm) { + ret = chsc_get_channel_measurement_chars(chp); + if (ret) + goto out_free; + } else { + static int msg_done; + + if (!msg_done) { + printk(KERN_WARNING "cio: Channel measurements not " + "available, continuing.\n"); + msg_done = 1; + } + chp->cmg = -1; + } /* make it known to the system */ ret = device_register(&chp->dev); @@ -1046,8 +1455,19 @@ new_channel_path(int chpid) if (ret) { device_unregister(&chp->dev); goto out_free; - } else - css[0]->chps[chpid] = chp; + } + mutex_lock(&css[0]->mutex); + if (css[0]->cm_enabled) { + ret = chsc_add_chp_cmg_attr(chp); + if (ret) { + sysfs_remove_group(&chp->dev.kobj, &chp_attr_group); + device_unregister(&chp->dev); + mutex_unlock(&css[0]->mutex); + goto out_free; + } + } + css[0]->chps[chpid] = chp; + mutex_unlock(&css[0]->mutex); return ret; out_free: kfree(chp); @@ -1070,7 +1490,6 @@ chsc_get_chp_desc(struct subchannel *sch, int chp_no) return desc; } - static int __init chsc_alloc_sei_area(void) { @@ -1103,10 +1522,8 @@ chsc_enable_facility(int operation_code) 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->request.length = 0x0400; + sda_area->request.code = 0x0031; sda_area->operation_code = operation_code; ret = chsc(sda_area); @@ -1161,10 +1578,8 @@ chsc_determine_css_characteristics(void) return -ENOMEM; } - scsc_area->request = (struct chsc_header) { - .length = 0x0010, - .code = 0x0010, - }; + scsc_area->request.length = 0x0010; + scsc_area->request.code = 0x0010; result = chsc(scsc_area); if (result) {