diff options
Diffstat (limited to 'drivers/s390/cio/chsc.c')
| -rw-r--r-- | drivers/s390/cio/chsc.c | 183 |
1 files changed, 117 insertions, 66 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 620a917cd3a1..fbb58edd6274 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -8,9 +8,9 @@ * Arnd Bergmann (arndb@de.ibm.com) */ -#define KMSG_COMPONENT "cio" -#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#define pr_fmt(fmt) "cio: " fmt +#include <linux/export.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/init.h> @@ -24,7 +24,6 @@ #include <asm/crw.h> #include <asm/isc.h> #include <asm/ebcdic.h> -#include <asm/ap.h> #include "css.h" #include "cio.h" @@ -40,6 +39,20 @@ static DEFINE_SPINLOCK(chsc_page_lock); #define SEI_VF_FLA 0xc0 /* VF flag for Full Link Address */ #define SEI_RS_CHPID 0x4 /* 4 in RS field indicates CHPID */ +static BLOCKING_NOTIFIER_HEAD(chsc_notifiers); + +int chsc_notifier_register(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&chsc_notifiers, nb); +} +EXPORT_SYMBOL(chsc_notifier_register); + +int chsc_notifier_unregister(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&chsc_notifiers, nb); +} +EXPORT_SYMBOL(chsc_notifier_unregister); + /** * chsc_error_from_response() - convert a chsc response to an error * @response: chsc response code @@ -191,7 +204,7 @@ EXPORT_SYMBOL_GPL(chsc_ssqd); * Returns 0 on success. */ int chsc_sadc(struct subchannel_id schid, struct chsc_scssc_area *scssc, - u64 summary_indicator_addr, u64 subchannel_indicator_addr, u8 isc) + dma64_t summary_indicator_addr, dma64_t subchannel_indicator_addr, u8 isc) { memset(scssc, 0, sizeof(*scssc)); scssc->request.length = 0x0fe0; @@ -219,16 +232,16 @@ EXPORT_SYMBOL_GPL(chsc_sadc); static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) { - spin_lock_irq(sch->lock); + spin_lock_irq(&sch->lock); if (sch->driver && sch->driver->chp_event) if (sch->driver->chp_event(sch, data, CHP_OFFLINE) != 0) goto out_unreg; - spin_unlock_irq(sch->lock); + spin_unlock_irq(&sch->lock); return 0; out_unreg: sch->lpm = 0; - spin_unlock_irq(sch->lock); + spin_unlock_irq(&sch->lock); css_schedule_eval(sch->schid); return 0; } @@ -239,7 +252,7 @@ void chsc_chp_offline(struct chp_id chpid) struct chp_link link; char dbf_txt[15]; - sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id); + scnprintf(dbf_txt, sizeof(dbf_txt), "chpr%x.%02x", chpid.cssid, chpid.id); CIO_TRACE_EVENT(2, dbf_txt); if (chp_get_status(chpid) <= 0) @@ -258,10 +271,10 @@ void chsc_chp_offline(struct chp_id chpid) static int __s390_process_res_acc(struct subchannel *sch, void *data) { - spin_lock_irq(sch->lock); + spin_lock_irq(&sch->lock); if (sch->driver && sch->driver->chp_event) sch->driver->chp_event(sch, data, CHP_ONLINE); - spin_unlock_irq(sch->lock); + spin_unlock_irq(&sch->lock); return 0; } @@ -270,11 +283,11 @@ static void s390_process_res_acc(struct chp_link *link) { char dbf_txt[15]; - sprintf(dbf_txt, "accpr%x.%02x", link->chpid.cssid, - link->chpid.id); + scnprintf(dbf_txt, sizeof(dbf_txt), "accpr%x.%02x", link->chpid.cssid, + link->chpid.id); CIO_TRACE_EVENT( 2, dbf_txt); if (link->fla != 0) { - sprintf(dbf_txt, "fla%x", link->fla); + scnprintf(dbf_txt, sizeof(dbf_txt), "fla%x", link->fla); CIO_TRACE_EVENT( 2, dbf_txt); } /* Wait until previous actions have settled. */ @@ -292,10 +305,10 @@ static void s390_process_res_acc(struct chp_link *link) static int process_fces_event(struct subchannel *sch, void *data) { - spin_lock_irq(sch->lock); + spin_lock_irq(&sch->lock); if (sch->driver && sch->driver->chp_event) sch->driver->chp_event(sch, data, CHP_FCES_EVENT); - spin_unlock_irq(sch->lock); + spin_unlock_irq(&sch->lock); return 0; } @@ -363,7 +376,7 @@ struct lir { #define PARAMS_LEN 10 /* PARAMS=xx,xxxxxx */ #define NODEID_LEN 35 /* NODEID=tttttt/mdl,mmm.ppssssssssssss,xxxx */ -/* Copy EBCIDC text, convert to ASCII and optionally add delimiter. */ +/* Copy EBCDIC text, convert to ASCII and optionally add delimiter. */ static char *store_ebcdic(char *dest, const char *src, unsigned long len, char delim) { @@ -393,8 +406,8 @@ static void format_node_data(char *params, char *id, struct node_descriptor *nd) memset(id, 0, NODEID_LEN); if (nd->validity != ND_VALIDITY_VALID) { - strncpy(params, "n/a", PARAMS_LEN - 1); - strncpy(id, "n/a", NODEID_LEN - 1); + strscpy(params, "n/a", PARAMS_LEN); + strscpy(id, "n/a", NODEID_LEN); return; } @@ -581,7 +594,8 @@ static void chsc_process_sei_ap_cfg_chg(struct chsc_sei_nt0_area *sei_area) if (sei_area->rs != 5) return; - ap_bus_cfg_chg(); + blocking_notifier_call_chain(&chsc_notifiers, + CHSC_NOTIFY_AP_CFG, NULL); } static void chsc_process_sei_fces_event(struct chsc_sei_nt0_area *sei_area) @@ -742,7 +756,7 @@ void chsc_chp_online(struct chp_id chpid) struct chp_link link; char dbf_txt[15]; - sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); + scnprintf(dbf_txt, sizeof(dbf_txt), "cadd%x.%02x", chpid.cssid, chpid.id); CIO_TRACE_EVENT(2, dbf_txt); if (chp_get_status(chpid) != 0) { @@ -769,11 +783,11 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch, memset(&link, 0, sizeof(struct chp_link)); link.chpid = chpid; - spin_lock_irqsave(sch->lock, flags); + spin_lock_irqsave(&sch->lock, flags); if (sch->driver && sch->driver->chp_event) sch->driver->chp_event(sch, &link, on ? CHP_VARY_ON : CHP_VARY_OFF); - spin_unlock_irqrestore(sch->lock, flags); + spin_unlock_irqrestore(&sch->lock, flags); } static int s390_subchannel_vary_chpid_off(struct subchannel *sch, void *data) @@ -844,7 +858,7 @@ chsc_add_cmg_attr(struct channel_subsystem *css) } return ret; cleanup: - for (--i; i >= 0; i--) { + while (i--) { if (!css->chps[i]) continue; chp_remove_cmg_attr(css->chps[i]); @@ -857,22 +871,22 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable) struct { struct chsc_header request; u32 operation_code : 2; - u32 : 30; + u32 : 1; + u32 e : 1; + u32 : 28; u32 key : 4; u32 : 28; - u32 zeroes1; - u32 cub_addr1; - u32 zeroes2; - u32 cub_addr2; - u32 reserved[13]; + dma64_t cub[CSS_NUM_CUB_PAGES]; + dma64_t ecub[CSS_NUM_ECUB_PAGES]; + u32 reserved[5]; struct chsc_header response; u32 status : 8; u32 : 4; u32 fmt : 4; u32 : 16; - } *secm_area; + } __packed *secm_area; unsigned long flags; - int ret, ccode; + int ret, ccode, i; spin_lock_irqsave(&chsc_page_lock, flags); memset(chsc_page, 0, PAGE_SIZE); @@ -881,8 +895,12 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable) secm_area->request.code = 0x0016; secm_area->key = PAGE_DEFAULT_KEY >> 4; - secm_area->cub_addr1 = (u64)(unsigned long)css->cub_addr1; - secm_area->cub_addr2 = (u64)(unsigned long)css->cub_addr2; + secm_area->e = 1; + + for (i = 0; i < CSS_NUM_CUB_PAGES; i++) + secm_area->cub[i] = (__force dma64_t)virt_to_dma32(css->cub[i]); + for (i = 0; i < CSS_NUM_ECUB_PAGES; i++) + secm_area->ecub[i] = virt_to_dma64(css->ecub[i]); secm_area->operation_code = enable ? 0 : 1; @@ -908,19 +926,47 @@ out: return ret; } +static int cub_alloc(struct channel_subsystem *css) +{ + int i; + + for (i = 0; i < CSS_NUM_CUB_PAGES; i++) { + css->cub[i] = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!css->cub[i]) + return -ENOMEM; + } + for (i = 0; i < CSS_NUM_ECUB_PAGES; i++) { + css->ecub[i] = (void *)get_zeroed_page(GFP_KERNEL); + if (!css->ecub[i]) + return -ENOMEM; + } + + return 0; +} + +static void cub_free(struct channel_subsystem *css) +{ + int i; + + for (i = 0; i < CSS_NUM_CUB_PAGES; i++) { + free_page((unsigned long)css->cub[i]); + css->cub[i] = NULL; + } + for (i = 0; i < CSS_NUM_ECUB_PAGES; i++) { + free_page((unsigned long)css->ecub[i]); + css->ecub[i] = NULL; + } +} + int chsc_secm(struct channel_subsystem *css, int enable) { int ret; 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); - return -ENOMEM; - } + ret = cub_alloc(css); + if (ret) + goto out; } ret = __chsc_do_secm(css, enable); if (!ret) { @@ -934,10 +980,11 @@ chsc_secm(struct channel_subsystem *css, int enable) } else chsc_remove_cmg_attr(css); } - if (!css->cm_enabled) { - free_page((unsigned long)css->cub_addr1); - free_page((unsigned long)css->cub_addr2); - } + +out: + if (!css->cm_enabled) + cub_free(css); + return ret; } @@ -1019,6 +1066,18 @@ chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, } } +static unsigned long scmc_get_speed(u32 s, u32 p) +{ + unsigned long speed = s; + + if (!p) + p = 8; + while (p--) + speed *= 10; + + return speed; +} + int chsc_get_channel_measurement_chars(struct channel_path *chp) { unsigned long flags; @@ -1033,20 +1092,13 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp) 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]; + struct cmg_cmcb cmcb; } *scmc_area; chp->shared = -1; chp->cmg = -1; + chp->extended = 0; + chp->speed = 0; if (!css_chsc_characteristics.scmc || !css_chsc_characteristics.secm) return -EINVAL; @@ -1071,17 +1123,16 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp) scmc_area->response.code); goto out; } - if (scmc_area->not_valid) + chp->cmcb = scmc_area->cmcb; + if (scmc_area->cmcb.not_valid) goto out; - chp->cmg = scmc_area->cmg; - chp->shared = scmc_area->shared; - if (chp->cmg != 2 && chp->cmg != 3) { - /* No cmg-dependent data. */ - goto out; - } - chsc_initialize_cmg_chars(chp, scmc_area->cmcv, - (struct cmg_chars *) &scmc_area->data); + chp->cmg = scmc_area->cmcb.cmg; + chp->shared = scmc_area->cmcb.shared; + chp->extended = scmc_area->cmcb.extended; + chp->speed = scmc_get_speed(scmc_area->cmcb.cmgs, scmc_area->cmcb.cmgp); + chsc_initialize_cmg_chars(chp, scmc_area->cmcb.cmcv, + (struct cmg_chars *)&scmc_area->cmcb.data); out: spin_unlock_irqrestore(&chsc_page_lock, flags); return ret; @@ -1091,8 +1142,8 @@ int __init chsc_init(void) { int ret; - sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + sei_page = (void *)get_zeroed_page(GFP_KERNEL); + chsc_page = (void *)get_zeroed_page(GFP_KERNEL); if (!sei_page || !chsc_page) { ret = -ENOMEM; goto out_err; @@ -1171,7 +1222,7 @@ int __init chsc_get_cssid_iid(int idx, u8 *cssid, u8 *iid) u8 cssid; u8 iid; u32 : 16; - } list[0]; + } list[]; } *sdcal_area; int ret; |
