From 00fd2cb0ec4208a32272b4c2f1063ab99a76473c Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Thu, 2 Jul 2015 09:06:55 +0200 Subject: s390/sclp: Change SCLP console default buffer-full behavior Dropping kernel messages during a console-buffer-full condition is preferable to halting the system until console messages are delivered, especially for production systems. Update default for sclp_console_drop kernel parameter accordingly. Signed-off-by: Peter Oberparleiter Acked-by: Martin Schwidefsky Signed-off-by: Martin Schwidefsky --- drivers/s390/char/sclp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 5e20513..f58bf4c 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -53,7 +53,7 @@ static DECLARE_COMPLETION(sclp_request_queue_flushed); /* Number of console pages to allocate, used by sclp_con.c and sclp_vt220.c */ int sclp_console_pages = SCLP_CONSOLE_PAGES; /* Flag to indicate if buffer pages are dropped on buffer full condition */ -int sclp_console_drop = 0; +int sclp_console_drop = 1; /* Number of times the console dropped buffer pages */ unsigned long sclp_console_full; @@ -79,8 +79,8 @@ static int __init sclp_setup_console_drop(char *str) int drop, rc; rc = kstrtoint(str, 0, &drop); - if (!rc && drop) - sclp_console_drop = 1; + if (!rc) + sclp_console_drop = drop; return 1; } -- cgit v1.1 From b1865401cf54c7ce06bccccb917f57c46a83b399 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Thu, 2 Jul 2015 14:00:30 +0200 Subject: s390/cio: Fix comma After dutifully acting as statement separator for 6 long years, this comma has finally grown into a full semicolon. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index f3c4179..6acd0b5 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -540,7 +540,7 @@ int ccw_device_stlck(struct ccw_device *cdev) if (rc) goto out_unlock; /* Perform operation. */ - cdev->private->state = DEV_STATE_STEAL_LOCK, + cdev->private->state = DEV_STATE_STEAL_LOCK; ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]); spin_unlock_irq(sch->lock); /* Wait for operation to finish. */ -- cgit v1.1 From 1d2334cb7da37e4b0005ca1d194d4e10ca7119f4 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Wed, 15 Jul 2015 14:04:18 +0200 Subject: s390/cio: Implement proper Link Incident Record handling A hardware problem on a FICON link is reported by the Channel Subsystem to the operating system via a Link Incident Record (LIR). In response, the operating system should issue a message that enables hardware service personnel to identify and repair the failing component. Current Linux LIR handling is broken because LIR data is incorrectly interpreted and no log message is generated. This patch fixes Linux LIR handling by implementing a new log message for LIRs indicating a degraded or non-operational link. Also LIRs are no longer used to deactivate channel paths because the available data does not reliably allow to determine the affected channel path. Signed-off-by: Peter Oberparleiter Reviewed-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chsc.c | 165 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 123 insertions(+), 42 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index e3bf885..548a189 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "css.h" #include "cio.h" @@ -272,36 +273,6 @@ static void s390_process_res_acc(struct chp_link *link) css_schedule_reprobe(); } -static int -__get_chpid_from_lir(void *data) -{ - struct lir { - u8 iq; - u8 ic; - u16 sci; - /* incident-node descriptor */ - u32 indesc[28]; - /* attached-node descriptor */ - u32 andesc[28]; - /* incident-specific information */ - u32 isinfo[28]; - } __attribute__ ((packed)) *lir; - - lir = data; - if (!(lir->iq&0x80)) - /* NULL link incident record */ - return -EINVAL; - if (!(lir->indesc[0]&0xc0000000)) - /* node descriptor not valid */ - return -EINVAL; - if (!(lir->indesc[0]&0x10000000)) - /* don't handle device-type nodes - FIXME */ - return -EINVAL; - /* Byte 3 contains the chpid. Could also be CTCA, but we don't care */ - - return (u16) (lir->indesc[0]&0x000000ff); -} - struct chsc_sei_nt0_area { u8 flags; u8 vf; /* validity flags */ @@ -341,22 +312,132 @@ struct chsc_sei { } u; } __packed; +/* + * Node Descriptor as defined in SA22-7204, "Common I/O-Device Commands" + */ + +#define ND_VALIDITY_VALID 0 +#define ND_VALIDITY_OUTDATED 1 +#define ND_VALIDITY_INVALID 2 + +struct node_descriptor { + /* Flags. */ + union { + struct { + u32 validity:3; + u32 reserved:5; + } __packed; + u8 byte0; + } __packed; + + /* Node parameters. */ + u32 params:24; + + /* Node ID. */ + char type[6]; + char model[3]; + char manufacturer[3]; + char plant[2]; + char seq[12]; + u16 tag; +} __packed; + +/* + * Link Incident Record as defined in SA22-7202, "ESCON I/O Interface" + */ + +#define LIR_IQ_CLASS_INFO 0 +#define LIR_IQ_CLASS_DEGRADED 1 +#define LIR_IQ_CLASS_NOT_OPERATIONAL 2 + +struct lir { + struct { + u32 null:1; + u32 reserved:3; + u32 class:2; + u32 reserved2:2; + } __packed iq; + u32 ic:8; + u32 reserved:16; + struct node_descriptor incident_node; + struct node_descriptor attached_node; + u8 reserved2[32]; +} __packed; + +#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. */ +static char *store_ebcdic(char *dest, const char *src, unsigned long len, + char delim) +{ + memcpy(dest, src, len); + EBCASC(dest, len); + + if (delim) + dest[len++] = delim; + + return dest + len; +} + +/* Format node ID and parameters for output in LIR log message. */ +static void format_node_data(char *params, char *id, struct node_descriptor *nd) +{ + memset(params, 0, PARAMS_LEN); + 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); + return; + } + + /* PARAMS=xx,xxxxxx */ + snprintf(params, PARAMS_LEN, "%02x,%06x", nd->byte0, nd->params); + /* NODEID=tttttt/mdl,mmm.ppssssssssssss,xxxx */ + id = store_ebcdic(id, nd->type, sizeof(nd->type), '/'); + id = store_ebcdic(id, nd->model, sizeof(nd->model), ','); + id = store_ebcdic(id, nd->manufacturer, sizeof(nd->manufacturer), '.'); + id = store_ebcdic(id, nd->plant, sizeof(nd->plant), 0); + id = store_ebcdic(id, nd->seq, sizeof(nd->seq), ','); + sprintf(id, "%04X", nd->tag); +} + static void chsc_process_sei_link_incident(struct chsc_sei_nt0_area *sei_area) { - struct chp_id chpid; - int id; + struct lir *lir = (struct lir *) &sei_area->ccdf; + char iuparams[PARAMS_LEN], iunodeid[NODEID_LEN], auparams[PARAMS_LEN], + aunodeid[NODEID_LEN]; - CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n", - sei_area->rs, sei_area->rsid); - if (sei_area->rs != 4) + CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x, iq=%02x)\n", + sei_area->rs, sei_area->rsid, sei_area->ccdf[0]); + + /* Ignore NULL Link Incident Records. */ + if (lir->iq.null) return; - id = __get_chpid_from_lir(sei_area->ccdf); - if (id < 0) - CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n"); - else { - chp_id_init(&chpid); - chpid.id = id; - chsc_chp_offline(chpid); + + /* Inform user that a link requires maintenance actions because it has + * become degraded or not operational. Note that this log message is + * the primary intention behind a Link Incident Record. */ + + format_node_data(iuparams, iunodeid, &lir->incident_node); + format_node_data(auparams, aunodeid, &lir->attached_node); + + switch (lir->iq.class) { + case LIR_IQ_CLASS_DEGRADED: + pr_warn("Link degraded: RS=%02x RSID=%04x IC=%02x " + "IUPARAMS=%s IUNODEID=%s AUPARAMS=%s AUNODEID=%s\n", + sei_area->rs, sei_area->rsid, lir->ic, iuparams, + iunodeid, auparams, aunodeid); + break; + case LIR_IQ_CLASS_NOT_OPERATIONAL: + pr_err("Link stopped: RS=%02x RSID=%04x IC=%02x " + "IUPARAMS=%s IUNODEID=%s AUPARAMS=%s AUNODEID=%s\n", + sei_area->rs, sei_area->rsid, lir->ic, iuparams, + iunodeid, auparams, aunodeid); + break; + default: + break; } } -- cgit v1.1 From c29a7baf091fc6b2c9e40561030f8c62e6145a19 Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Thu, 6 Mar 2014 18:47:21 +0100 Subject: s390/numa: add emulation support NUMA emulation (aka fake NUMA) distributes the available memory to nodes without using real topology information about the physical memory of the machine. Splitting the system memory into nodes replicates the memory management structures for each node. Particularly each node has its own "mm locks" and its own "kswapd" task. For large systems, under certain conditions, this results in improved system performance and/or latency based on reduced pressure on the mm locks and the kswapd tasks. NUMA emulation distributes CPUs to nodes while respecting the original machine topology information. This is done by trying to avoid to separate CPUs which reside on the same book or even on the same MC. Because the current Linux scheduler code requires a stable cpu to node mapping, cores are pinned to nodes when the first CPU thread is set online. This patch is based on the initial implementation from Philipp Hachtmann. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky --- drivers/s390/char/sclp_cmd.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index e9485fb..806239c 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "sclp.h" @@ -388,11 +389,11 @@ static struct notifier_block sclp_mem_nb = { }; static void __init align_to_block_size(unsigned long long *start, - unsigned long long *size) + unsigned long long *size, + unsigned long long alignment) { - unsigned long long start_align, size_align, alignment; + unsigned long long start_align, size_align; - alignment = memory_block_size_bytes(); start_align = roundup(*start, alignment); size_align = rounddown(*start + *size, alignment) - start_align; @@ -404,8 +405,8 @@ static void __init align_to_block_size(unsigned long long *start, static void __init add_memory_merged(u16 rn) { + unsigned long long start, size, addr, block_size; static u16 first_rn, num; - unsigned long long start, size; if (rn && first_rn && (first_rn + num == rn)) { num++; @@ -423,9 +424,12 @@ static void __init add_memory_merged(u16 rn) goto skip_add; if (memory_end_set && (start + size > memory_end)) size = memory_end - start; - align_to_block_size(&start, &size); - if (size) - add_memory(0, start, size); + block_size = memory_block_size_bytes(); + align_to_block_size(&start, &size, block_size); + if (!size) + goto skip_add; + for (addr = start; addr < start + size; addr += block_size) + add_memory(numa_pfn_to_nid(PFN_DOWN(addr)), addr, block_size); skip_add: first_rn = rn; num = 1; -- cgit v1.1 From 3b97487445ef3833fcb88ca4bb469b6df3b44329 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Tue, 4 Aug 2015 17:11:15 +0200 Subject: s390: remove unneeded semicolon Remove unneeded semicolon. The semantic patch that detects this change is available at scripts/coccinelle/misc/semicolon.cocci. Signed-off-by: Peter Senna Tschudin Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_alias.c | 6 +++--- drivers/s390/char/monreader.c | 2 +- drivers/s390/scsi/zfcp_fsf.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index ee3a6fa..fe07f31 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c @@ -58,7 +58,7 @@ static struct alias_server *_find_server(struct dasd_uid *uid) && !strncmp(pos->uid.serial, uid->serial, sizeof(uid->serial))) return pos; - }; + } return NULL; } @@ -69,7 +69,7 @@ static struct alias_lcu *_find_lcu(struct alias_server *server, list_for_each_entry(pos, &server->lculist, lcu) { if (pos->uid.ssid == uid->ssid) return pos; - }; + } return NULL; } @@ -97,7 +97,7 @@ static struct alias_pav_group *_find_group(struct alias_lcu *lcu, if (pos->uid.base_unit_addr == search_unit_addr && !strncmp(pos->uid.vduit, uid->vduit, sizeof(uid->vduit))) return pos; - }; + } return NULL; } diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 0da3ae3..b7d6030 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -95,7 +95,7 @@ static void dcss_mkname(char *ascii_name, char *ebcdic_name) if (ascii_name[i] == '\0') break; ebcdic_name[i] = toupper(ascii_name[i]); - }; + } for (; i < 8; i++) ebcdic_name[i] = ' '; ASCEBC(ebcdic_name, 8); diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 21ec5e2..4ac73e0 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -204,7 +204,7 @@ static void zfcp_fsf_status_read_link_down(struct zfcp_fsf_req *req) break; case FSF_STATUS_READ_SUB_FIRMWARE_UPDATE: zfcp_fsf_link_down_info_eval(req, NULL); - }; + } } static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req) -- cgit v1.1 From b2f4de8b843567504ee12b652b35819b60e25a86 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Tue, 4 Aug 2015 17:11:47 +0200 Subject: s390: remove unneeded variables This patch remove unneeded variables used to store return values. These issues were detected with the Coccinelle script: scripts/coccinelle/misc/returnvar.cocci [heiko.carstens@de.ibm.com]: make qeth_l[2/3]_stop() return void Signed-off-by: Peter Senna Tschudin Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/eadm_sch.c | 3 +-- drivers/s390/net/qeth_l2_main.c | 5 +---- drivers/s390/net/qeth_l3_main.c | 5 +---- 3 files changed, 3 insertions(+), 10 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/eadm_sch.c b/drivers/s390/cio/eadm_sch.c index bee8c11..b3f44bc 100644 --- a/drivers/s390/cio/eadm_sch.c +++ b/drivers/s390/cio/eadm_sch.c @@ -336,7 +336,6 @@ static int eadm_subchannel_sch_event(struct subchannel *sch, int process) { struct eadm_private *private; unsigned long flags; - int ret = 0; spin_lock_irqsave(sch->lock, flags); if (!device_is_registered(&sch->dev)) @@ -356,7 +355,7 @@ static int eadm_subchannel_sch_event(struct subchannel *sch, int process) out_unlock: spin_unlock_irqrestore(sch->lock, flags); - return ret; + return 0; } static struct css_device_id eadm_subchannel_ids[] = { diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 2e65b98..a855669 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -390,10 +390,8 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev, return rc; } -static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode) +static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode) { - int rc = 0; - QETH_DBF_TEXT(SETUP , 2, "stopcard"); QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); @@ -427,7 +425,6 @@ static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode) qeth_clear_cmd_buffers(&card->read); qeth_clear_cmd_buffers(&card->write); } - return rc; } static int qeth_l2_process_inbound_buffer(struct qeth_card *card, diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 70eb2f61..a1aaa36 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2158,10 +2158,8 @@ static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev) return card ; } -static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode) +static void qeth_l3_stop_card(struct qeth_card *card, int recovery_mode) { - int rc = 0; - QETH_DBF_TEXT(SETUP, 2, "stopcard"); QETH_DBF_HEX(SETUP, 2, &card, sizeof(void *)); @@ -2196,7 +2194,6 @@ static int qeth_l3_stop_card(struct qeth_card *card, int recovery_mode) qeth_clear_cmd_buffers(&card->read); qeth_clear_cmd_buffers(&card->write); } - return rc; } /* -- cgit v1.1 From 1eb38023ddb480276c6b75e0db6463c47251eb7b Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Fri, 7 Aug 2015 13:19:03 +0200 Subject: s390/dasd: fix failing path verification DASD path verification requires the usage of sleep_on_immediatly to ensure that no other I/O request is blocking the recovery of disconnected devices. But two concurrent path verification workers for the same device may kill each others requests due to the usage of the immediate sleep_on function. This may lead to unsuccessful path verifications. Prevent that two parallel path verification workers conflict with each other by implementing a device flag signalling a already running worker. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eckd.c | 8 ++++++-- drivers/s390/block/dasd_int.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 6215f64..d814451 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1259,7 +1259,11 @@ static void do_path_verification_work(struct work_struct *work) schedule_work(work); return; } - + /* check if path verification already running and delay if so */ + if (test_and_set_bit(DASD_FLAG_PATH_VERIFY, &device->flags)) { + schedule_work(work); + return; + } opm = 0; npm = 0; ppm = 0; @@ -1402,7 +1406,7 @@ static void do_path_verification_work(struct work_struct *work) device->path_data.hpfpm |= hpfpm; spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } - + clear_bit(DASD_FLAG_PATH_VERIFY, &device->flags); dasd_put_device(device); if (data->isglobal) mutex_unlock(&dasd_path_verification_mutex); diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 227e3de..4aed5ed 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -534,6 +534,7 @@ struct dasd_attention_data { #define DASD_FLAG_SAFE_OFFLINE 10 /* safe offline processing requested*/ #define DASD_FLAG_SAFE_OFFLINE_RUNNING 11 /* safe offline running */ #define DASD_FLAG_ABORTALL 12 /* Abort all noretry requests */ +#define DASD_FLAG_PATH_VERIFY 13 /* Path verification worker running */ #define DASD_SLEEPON_START_TAG ((void *) 1) #define DASD_SLEEPON_END_TAG ((void *) 2) -- cgit v1.1 From b179b037e568a27ab3e8f5bedea1f4455aba7378 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Fri, 7 Aug 2015 13:20:16 +0200 Subject: s390/dasd: enhance CUIR scope detection This patch adds an enhanced detection for control unit initiated reconfiguration request scope. The first approach assumed the scope of the reconfiguration request to be restricted to the path on which the message was received. The enhanced approach determines the full scope of the reconfiguration request by evaluating additional path and device selection information contained in the reconfiguration message. Reviewed-by: Peter Oberparleiter Reviewed-by: Sebastian Ott Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eckd.c | 323 +++++++++++++++++++++++++++++++---------- drivers/s390/block/dasd_eckd.h | 11 +- 2 files changed, 255 insertions(+), 79 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index d814451..72cdaae 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1036,7 +1036,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device) { void *conf_data; int conf_len, conf_data_saved; - int rc, path_err; + int rc, path_err, pos; __u8 lpm, opm; struct dasd_eckd_private *private, path_private; struct dasd_path *path_data; @@ -1068,6 +1068,17 @@ static int dasd_eckd_read_conf(struct dasd_device *device) path_data->opm |= lpm; continue; /* no error */ } + /* translate path mask to position in mask */ + pos = 8 - ffs(lpm); + kfree(private->path_conf_data[pos]); + if ((__u8 *)private->path_conf_data[pos] == + private->conf_data) { + private->conf_data = NULL; + private->conf_len = 0; + conf_data_saved = 0; + } + private->path_conf_data[pos] = + (struct dasd_conf_data *) conf_data; /* save first valid configuration data */ if (!conf_data_saved) { kfree(private->conf_data); @@ -1095,7 +1106,6 @@ static int dasd_eckd_read_conf(struct dasd_device *device) kfree(conf_data); continue; } - if (dasd_eckd_compare_path_uid( device, &path_private)) { uid = &path_private.uid; @@ -1157,9 +1167,6 @@ static int dasd_eckd_read_conf(struct dasd_device *device) path_data->cablepm &= ~lpm; path_data->hpfpm &= ~lpm; path_data->cuirpm &= ~lpm; - - if (conf_data != private->conf_data) - kfree(conf_data); } return path_err; @@ -1814,6 +1821,7 @@ out_err1: static void dasd_eckd_uncheck_device(struct dasd_device *device) { struct dasd_eckd_private *private; + int i; private = (struct dasd_eckd_private *) device->private; dasd_alias_disconnect_device_from_lcu(device); @@ -1822,6 +1830,15 @@ static void dasd_eckd_uncheck_device(struct dasd_device *device) private->vdsneq = NULL; private->gneq = NULL; private->conf_len = 0; + for (i = 0; i < 8; i++) { + kfree(private->path_conf_data[i]); + if ((__u8 *)private->path_conf_data[i] == + private->conf_data) { + private->conf_data = NULL; + private->conf_len = 0; + } + private->path_conf_data[i] = NULL; + } kfree(private->conf_data); private->conf_data = NULL; } @@ -4529,12 +4546,13 @@ static int dasd_eckd_read_message_buffer(struct dasd_device *device, cqr->startdev = device; cqr->memdev = device; cqr->block = NULL; - cqr->retries = 256; cqr->expires = 10 * HZ; - - /* we need to check for messages on exactly this path */ set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags); - cqr->lpm = lpum; + /* dasd_sleep_on_immediatly does not do complex error + * recovery so clear erp flag and set retry counter to + * do basic erp */ + clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); + cqr->retries = 256; /* Prepare for Read Subsystem Data */ prssdp = (struct dasd_psf_prssd_data *) cqr->data; @@ -4609,10 +4627,10 @@ dasd_eckd_psf_cuir_response(struct dasd_device *device, int response, psf_cuir->message_id = message_id; psf_cuir->cssid = sch_id.cssid; psf_cuir->ssid = sch_id.ssid; - ccw = cqr->cpaddr; ccw->cmd_code = DASD_ECKD_CCW_PSF; ccw->cda = (__u32)(addr_t)psf_cuir; + ccw->flags = CCW_FLAG_SLI; ccw->count = sizeof(struct dasd_psf_cuir_response); cqr->startdev = device; @@ -4622,6 +4640,7 @@ dasd_eckd_psf_cuir_response(struct dasd_device *device, int response, cqr->expires = 10*HZ; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; + set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags); rc = dasd_sleep_on(cqr); @@ -4629,118 +4648,252 @@ dasd_eckd_psf_cuir_response(struct dasd_device *device, int response, return rc; } -static int dasd_eckd_cuir_change_state(struct dasd_device *device, __u8 lpum) +/* + * return configuration data that is referenced by record selector + * if a record selector is specified or per default return the + * conf_data pointer for the path specified by lpum + */ +static struct dasd_conf_data *dasd_eckd_get_ref_conf(struct dasd_device *device, + __u8 lpum, + struct dasd_cuir_message *cuir) { - unsigned long flags; - __u8 tbcpm; + struct dasd_eckd_private *private; + struct dasd_conf_data *conf_data; + int path, pos; - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - tbcpm = device->path_data.opm & ~lpum; - if (tbcpm) { - device->path_data.opm = tbcpm; - device->path_data.cuirpm |= lpum; + private = (struct dasd_eckd_private *) device->private; + if (cuir->record_selector == 0) + goto out; + for (path = 0x80, pos = 0; path; path >>= 1, pos++) { + conf_data = private->path_conf_data[pos]; + if (conf_data->gneq.record_selector == + cuir->record_selector) + return conf_data; } - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); - return tbcpm ? 0 : PSF_CUIR_LAST_PATH; +out: + return private->path_conf_data[8 - ffs(lpum)]; } /* - * walk through all devices and quiesce them - * if it is the last path return error + * This function determines the scope of a reconfiguration request by + * analysing the path and device selection data provided in the CUIR request. + * Returns a path mask containing CUIR affected paths for the give device. + * + * If the CUIR request does not contain the required information return the + * path mask of the path the attention message for the CUIR request was reveived + * on. + */ +static int dasd_eckd_cuir_scope(struct dasd_device *device, __u8 lpum, + struct dasd_cuir_message *cuir) +{ + struct dasd_conf_data *ref_conf_data; + unsigned long bitmask = 0, mask = 0; + struct dasd_eckd_private *private; + struct dasd_conf_data *conf_data; + unsigned int pos, path; + char *ref_gneq, *gneq; + char *ref_ned, *ned; + int tbcpm = 0; + + /* if CUIR request does not specify the scope use the path + the attention message was presented on */ + if (!cuir->ned_map || + !(cuir->neq_map[0] | cuir->neq_map[1] | cuir->neq_map[2])) + return lpum; + + private = (struct dasd_eckd_private *) device->private; + /* get reference conf data */ + ref_conf_data = dasd_eckd_get_ref_conf(device, lpum, cuir); + /* reference ned is determined by ned_map field */ + pos = 8 - ffs(cuir->ned_map); + ref_ned = (char *)&ref_conf_data->neds[pos]; + ref_gneq = (char *)&ref_conf_data->gneq; + /* transfer 24 bit neq_map to mask */ + mask = cuir->neq_map[2]; + mask |= cuir->neq_map[1] << 8; + mask |= cuir->neq_map[0] << 16; + + for (path = 0x80; path; path >>= 1) { + /* initialise data per path */ + bitmask = mask; + pos = 8 - ffs(path); + conf_data = private->path_conf_data[pos]; + pos = 8 - ffs(cuir->ned_map); + ned = (char *) &conf_data->neds[pos]; + /* compare reference ned and per path ned */ + if (memcmp(ref_ned, ned, sizeof(*ned)) != 0) + continue; + gneq = (char *)&conf_data->gneq; + /* compare reference gneq and per_path gneq under + 24 bit mask where mask bit 0 equals byte 7 of + the gneq and mask bit 24 equals byte 31 */ + while (bitmask) { + pos = ffs(bitmask) - 1; + if (memcmp(&ref_gneq[31 - pos], &gneq[31 - pos], 1) + != 0) + break; + clear_bit(pos, &bitmask); + } + if (bitmask) + continue; + /* device and path match the reference values + add path to CUIR scope */ + tbcpm |= path; + } + return tbcpm; +} + +static void dasd_eckd_cuir_notify_user(struct dasd_device *device, + unsigned long paths, + struct subchannel_id sch_id, int action) +{ + struct channel_path_desc *desc; + int pos; + + while (paths) { + /* get position of bit in mask */ + pos = ffs(paths) - 1; + /* get channel path descriptor from this position */ + desc = ccw_device_get_chp_desc(device->cdev, 7 - pos); + if (action == CUIR_QUIESCE) + pr_warn("Service on the storage server caused path " + "%x.%02x to go offline", sch_id.cssid, + desc ? desc->chpid : 0); + else if (action == CUIR_RESUME) + pr_info("Path %x.%02x is back online after service " + "on the storage server", sch_id.cssid, + desc ? desc->chpid : 0); + kfree(desc); + clear_bit(pos, &paths); + } +} + +static int dasd_eckd_cuir_remove_path(struct dasd_device *device, __u8 lpum, + struct dasd_cuir_message *cuir) +{ + unsigned long tbcpm; + + tbcpm = dasd_eckd_cuir_scope(device, lpum, cuir); + /* nothing to do if path is not in use */ + if (!(device->path_data.opm & tbcpm)) + return 0; + if (!(device->path_data.opm & ~tbcpm)) { + /* no path would be left if the CUIR action is taken + return error */ + return -EINVAL; + } + /* remove device from operational path mask */ + device->path_data.opm &= ~tbcpm; + device->path_data.cuirpm |= tbcpm; + return tbcpm; +} + +/* + * walk through all devices and build a path mask to quiesce them + * return an error if the last path to a device would be removed * * if only part of the devices are quiesced and an error * occurs no onlining necessary, the storage server will * notify the already set offline devices again */ static int dasd_eckd_cuir_quiesce(struct dasd_device *device, __u8 lpum, - struct channel_path_desc *desc, - struct subchannel_id sch_id) + struct subchannel_id sch_id, + struct dasd_cuir_message *cuir) { struct alias_pav_group *pavgroup, *tempgroup; struct dasd_eckd_private *private; struct dasd_device *dev, *n; - int rc; + unsigned long paths = 0; + unsigned long flags; + int tbcpm; private = (struct dasd_eckd_private *) device->private; - rc = 0; - /* active devices */ - list_for_each_entry_safe(dev, n, - &private->lcu->active_devices, + list_for_each_entry_safe(dev, n, &private->lcu->active_devices, alias_list) { - rc = dasd_eckd_cuir_change_state(dev, lpum); - if (rc) - goto out; + spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags); + tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir); + spin_unlock_irqrestore(get_ccwdev_lock(dev->cdev), flags); + if (tbcpm < 0) + goto out_err; + paths |= tbcpm; } - /* inactive devices */ - list_for_each_entry_safe(dev, n, - &private->lcu->inactive_devices, + list_for_each_entry_safe(dev, n, &private->lcu->inactive_devices, alias_list) { - rc = dasd_eckd_cuir_change_state(dev, lpum); - if (rc) - goto out; + spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags); + tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir); + spin_unlock_irqrestore(get_ccwdev_lock(dev->cdev), flags); + if (tbcpm < 0) + goto out_err; + paths |= tbcpm; } - /* devices in PAV groups */ list_for_each_entry_safe(pavgroup, tempgroup, &private->lcu->grouplist, group) { list_for_each_entry_safe(dev, n, &pavgroup->baselist, alias_list) { - rc = dasd_eckd_cuir_change_state(dev, lpum); - if (rc) - goto out; + spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags); + tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir); + spin_unlock_irqrestore( + get_ccwdev_lock(dev->cdev), flags); + if (tbcpm < 0) + goto out_err; + paths |= tbcpm; } list_for_each_entry_safe(dev, n, &pavgroup->aliaslist, alias_list) { - rc = dasd_eckd_cuir_change_state(dev, lpum); - if (rc) - goto out; + spin_lock_irqsave(get_ccwdev_lock(dev->cdev), flags); + tbcpm = dasd_eckd_cuir_remove_path(dev, lpum, cuir); + spin_unlock_irqrestore( + get_ccwdev_lock(dev->cdev), flags); + if (tbcpm < 0) + goto out_err; + paths |= tbcpm; } } - - pr_warn("Service on the storage server caused path %x.%02x to go offline", - sch_id.cssid, desc ? desc->chpid : 0); - rc = PSF_CUIR_COMPLETED; -out: - return rc; + /* notify user about all paths affected by CUIR action */ + dasd_eckd_cuir_notify_user(device, paths, sch_id, CUIR_QUIESCE); + return 0; +out_err: + return tbcpm; } static int dasd_eckd_cuir_resume(struct dasd_device *device, __u8 lpum, - struct channel_path_desc *desc, - struct subchannel_id sch_id) + struct subchannel_id sch_id, + struct dasd_cuir_message *cuir) { struct alias_pav_group *pavgroup, *tempgroup; struct dasd_eckd_private *private; struct dasd_device *dev, *n; + unsigned long paths = 0; + int tbcpm; - pr_info("Path %x.%02x is back online after service on the storage server", - sch_id.cssid, desc ? desc->chpid : 0); private = (struct dasd_eckd_private *) device->private; - /* * the path may have been added through a generic path event before * only trigger path verification if the path is not already in use */ - list_for_each_entry_safe(dev, n, &private->lcu->active_devices, alias_list) { - if (!(dev->path_data.opm & lpum)) { - dev->path_data.tbvpm |= lpum; + tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir); + paths |= tbcpm; + if (!(dev->path_data.opm & tbcpm)) { + dev->path_data.tbvpm |= tbcpm; dasd_schedule_device_bh(dev); } } - list_for_each_entry_safe(dev, n, &private->lcu->inactive_devices, alias_list) { - if (!(dev->path_data.opm & lpum)) { - dev->path_data.tbvpm |= lpum; + tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir); + paths |= tbcpm; + if (!(dev->path_data.opm & tbcpm)) { + dev->path_data.tbvpm |= tbcpm; dasd_schedule_device_bh(dev); } } - /* devices in PAV groups */ list_for_each_entry_safe(pavgroup, tempgroup, &private->lcu->grouplist, @@ -4748,21 +4901,27 @@ static int dasd_eckd_cuir_resume(struct dasd_device *device, __u8 lpum, list_for_each_entry_safe(dev, n, &pavgroup->baselist, alias_list) { - if (!(dev->path_data.opm & lpum)) { - dev->path_data.tbvpm |= lpum; + tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir); + paths |= tbcpm; + if (!(dev->path_data.opm & tbcpm)) { + dev->path_data.tbvpm |= tbcpm; dasd_schedule_device_bh(dev); } } list_for_each_entry_safe(dev, n, &pavgroup->aliaslist, alias_list) { - if (!(dev->path_data.opm & lpum)) { - dev->path_data.tbvpm |= lpum; + tbcpm = dasd_eckd_cuir_scope(dev, lpum, cuir); + paths |= tbcpm; + if (!(dev->path_data.opm & tbcpm)) { + dev->path_data.tbvpm |= tbcpm; dasd_schedule_device_bh(dev); } } } - return PSF_CUIR_COMPLETED; + /* notify user about all paths affected by CUIR action */ + dasd_eckd_cuir_notify_user(device, paths, sch_id, CUIR_RESUME); + return 0; } static void dasd_eckd_handle_cuir(struct dasd_device *device, void *messages, @@ -4772,8 +4931,12 @@ static void dasd_eckd_handle_cuir(struct dasd_device *device, void *messages, struct channel_path_desc *desc; struct subchannel_id sch_id; int pos, response; - ccw_device_get_schid(device->cdev, &sch_id); + DBF_DEV_EVENT(DBF_WARNING, device, + "CUIR request: %016llx %016llx %016llx %08x", + ((u64 *)cuir)[0], ((u64 *)cuir)[1], ((u64 *)cuir)[2], + ((u32 *)cuir)[3]); + ccw_device_get_schid(device->cdev, &sch_id); /* get position of path in mask */ pos = 8 - ffs(lpum); /* get channel path descriptor from this position */ @@ -4781,18 +4944,26 @@ static void dasd_eckd_handle_cuir(struct dasd_device *device, void *messages, if (cuir->code == CUIR_QUIESCE) { /* quiesce */ - response = dasd_eckd_cuir_quiesce(device, lpum, desc, sch_id); + if (dasd_eckd_cuir_quiesce(device, lpum, sch_id, cuir)) + response = PSF_CUIR_LAST_PATH; + else + response = PSF_CUIR_COMPLETED; } else if (cuir->code == CUIR_RESUME) { /* resume */ - response = dasd_eckd_cuir_resume(device, lpum, desc, sch_id); + dasd_eckd_cuir_resume(device, lpum, sch_id, cuir); + response = PSF_CUIR_COMPLETED; } else response = PSF_CUIR_NOT_SUPPORTED; - dasd_eckd_psf_cuir_response(device, response, cuir->message_id, - desc, sch_id); - + dasd_eckd_psf_cuir_response(device, response, + cuir->message_id, desc, sch_id); + DBF_DEV_EVENT(DBF_WARNING, device, + "CUIR response: %d on message ID %08x", response, + cuir->message_id); /* free descriptor copy */ kfree(desc); + /* to make sure there is no attention left schedule work again */ + device->discipline->check_attention(device, lpum); } static void dasd_eckd_check_attention_work(struct work_struct *work) @@ -4804,22 +4975,18 @@ static void dasd_eckd_check_attention_work(struct work_struct *work) data = container_of(work, struct check_attention_work_data, worker); device = data->device; - messages = kzalloc(sizeof(*messages), GFP_KERNEL); if (!messages) { DBF_DEV_EVENT(DBF_WARNING, device, "%s", "Could not allocate attention message buffer"); goto out; } - rc = dasd_eckd_read_message_buffer(device, messages, data->lpum); if (rc) goto out; - if (messages->length == ATTENTION_LENGTH_CUIR && messages->format == ATTENTION_FORMAT_CUIR) dasd_eckd_handle_cuir(device, messages, data->lpum); - out: dasd_put_device(device); kfree(messages); diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index ddab7df..f8f91ee 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h @@ -355,7 +355,8 @@ struct dasd_gneq { __u8 identifier:2; __u8 reserved:6; } __attribute__ ((packed)) flags; - __u8 reserved[5]; + __u8 record_selector; + __u8 reserved[4]; struct { __u8 value:2; __u8 number:6; @@ -492,10 +493,18 @@ struct alias_pav_group { struct dasd_device *next; }; +struct dasd_conf_data { + struct dasd_ned neds[5]; + u8 reserved[64]; + struct dasd_gneq gneq; +} __packed; + struct dasd_eckd_private { struct dasd_eckd_characteristics rdc_data; u8 *conf_data; int conf_len; + /* per path configuration data */ + struct dasd_conf_data *path_conf_data[8]; /* pointers to specific parts in the conf_data */ struct dasd_ned *ned; struct dasd_sneq *sneq; -- cgit v1.1 From 92d6289105d134582094eede00192bd66f54905f Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 13 Aug 2015 13:26:49 +0200 Subject: s390: remove unneeded sizeof(void *) comparisons Remove two more statements which always evaluate to 'false'. These are more leftovers from the 31 bit era. Signed-off-by: Heiko Carstens Reviewed-by: David Hildenbrand Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eckd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 72cdaae..62a3235 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -3989,7 +3989,7 @@ static int dasd_symm_io(struct dasd_device *device, void __user *argp) rc = -EFAULT; if (copy_from_user(&usrparm, argp, sizeof(usrparm))) goto out; - if (is_compat_task() || sizeof(long) == 4) { + if (is_compat_task()) { /* Make sure pointers are sane even on 31 bit. */ rc = -EINVAL; if ((usrparm.psf_data >> 32) != 0) -- cgit v1.1 From e619cd3d618672bea8602d682ea63b42a1faf1bc Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 17 Aug 2015 07:56:20 +0200 Subject: s390/hmcdrv: fix interrupt registration The z/VM driver sets bit "63-22" in control register zero to one in order to enable the CP Service interrupt (0x2603). However the irq subclass mask that normally corresponds to the CP Service interrupt is "63-54" (== "63-22-32"). So it looks like the author read the documentation with the 32 bit sized cr0 register bit positions (== 22), but didn't realize that bit numbers change, if applied to a 64 bit register (== 54) due to the numbering scheme. Also use irq_subclass_register() instead if ctl_set_bit() since multiple services depend on the service signal subclass mask, which is the correct bit. This also explains why nobody noticed the bug, since the bit is always enabled anyway (e.g. pfault). Signed-off-by: Heiko Carstens Reviewed-by: Hendrik Brueckner Acked-by: Martin Schwidefsky Signed-off-by: Martin Schwidefsky --- drivers/s390/char/diag_ftp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/char/diag_ftp.c b/drivers/s390/char/diag_ftp.c index 9388963..12db8db 100644 --- a/drivers/s390/char/diag_ftp.c +++ b/drivers/s390/char/diag_ftp.c @@ -223,7 +223,7 @@ int diag_ftp_startup(void) if (rc) return rc; - ctl_set_bit(0, 63 - 22); + irq_subclass_register(IRQ_SUBCLASS_SERVICE_SIGNAL); return 0; } @@ -232,6 +232,6 @@ int diag_ftp_startup(void) */ void diag_ftp_shutdown(void) { - ctl_clear_bit(0, 63 - 22); + irq_subclass_unregister(IRQ_SUBCLASS_SERVICE_SIGNAL); unregister_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler); } -- cgit v1.1 From e4e1899aaec0370aea6b98597ee6289d60544ae2 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 17 Aug 2015 07:59:47 +0200 Subject: s390/zcrypt: use msleep() instead of mdelay() There is no need to busy loop and monopolize a cpu for up to ~2 seconds. The code in question that calls mdelay() is preemptible anyway, so better let the kernel schedule different processes than just looping and causing unnecessary delays. Signed-off-by: Heiko Carstens Acked-by: Martin Schwidefsky Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 2 +- drivers/s390/crypto/zcrypt_pcixcc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 559a9dc..d78b3d6 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1372,7 +1372,7 @@ static int ap_probe_device_type(struct ap_device *ap_dev) /* Wait for the test message to complete. */ for (i = 0; i < 6; i++) { - mdelay(300); + msleep(300); status = __ap_recv(ap_dev->qid, &psmid, reply, 4096); if (status.response_code == AP_RESPONSE_NORMAL && psmid == 0x0102030405060708ULL) diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index 899ffa1..f418527 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -182,7 +182,7 @@ static int zcrypt_pcixcc_mcl(struct ap_device *ap_dev) /* Wait for the test message to complete. */ for (i = 0; i < 6; i++) { - mdelay(300); + msleep(300); rc = ap_recv(ap_dev->qid, &psmid, reply, 4096); if (rc == 0 && psmid == 0x0102030405060708ULL) break; -- cgit v1.1 From 42cfc6b590c5eb7ae3c1de89594f8f2193ecbc97 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Wed, 19 Aug 2015 10:42:23 +0200 Subject: s390/dcssblk: correct out of bounds array indexes Fix a couple of warnings like this: [linux-4.2-rc7/drivers/s390/block/dcssblk.c:553]: (style) Array index 'j' is used before limits check. Reported-by: David Binderman Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dcssblk.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index da21281..dff3fcb 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -548,10 +548,10 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char */ num_of_segments = 0; for (i = 0; (i < count && (buf[i] != '\0') && (buf[i] != '\n')); i++) { - for (j = i; (buf[j] != ':') && + for (j = i; j < count && + (buf[j] != ':') && (buf[j] != '\0') && - (buf[j] != '\n') && - j < count; j++) { + (buf[j] != '\n'); j++) { local_buf[j-i] = toupper(buf[j]); } local_buf[j-i] = '\0'; @@ -723,7 +723,7 @@ dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const ch /* * parse input */ - for (i = 0; ((*(buf+i)!='\0') && (*(buf+i)!='\n') && i < count); i++) { + for (i = 0; (i < count && (*(buf+i)!='\0') && (*(buf+i)!='\n')); i++) { local_buf[i] = toupper(buf[i]); } local_buf[i] = '\0'; @@ -904,10 +904,10 @@ dcssblk_check_params(void) for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0'); i++) { - for (j = i; (dcssblk_segments[j] != ',') && + for (j = i; (j < DCSSBLK_PARM_LEN) && + (dcssblk_segments[j] != ',') && (dcssblk_segments[j] != '\0') && - (dcssblk_segments[j] != '(') && - (j < DCSSBLK_PARM_LEN); j++) + (dcssblk_segments[j] != '('); j++) { buf[j-i] = dcssblk_segments[j]; } -- cgit v1.1 From 05bfd70bcdd3cd12c061cb77b73a11ba6f87379d Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Wed, 19 Aug 2015 16:50:10 +0200 Subject: s390/3270: redraw screen on unsolicited device end If a 3270 terminal is disconnected and later reconnected again, it gets an unsolicited device end. This is currently ignored and you have to hit the clear key to get the screen redrawn. Add an automatic full redraw of the screen for this case. Signed-off-by: Martin Schwidefsky --- drivers/s390/char/con3270.c | 4 ++++ drivers/s390/char/tty3270.c | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'drivers/s390') diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 75ffe99..7c511ad 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -413,6 +413,10 @@ con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb) else /* Normal end. Copy residual count. */ rq->rescnt = irb->scsw.cmd.count; + } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { + /* Interrupt without an outstanding request -> update all */ + cp->update_flags = CON_UPDATE_ALL; + con3270_set_timer(cp, 1); } return RAW3270_IO_DONE; } diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index e91b89d..e96fc7f 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -659,6 +659,10 @@ tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb) else /* Normal end. Copy residual count. */ rq->rescnt = irb->scsw.cmd.count; + } else if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) { + /* Interrupt without an outstanding request -> update all */ + tp->update_flags = TTY_UPDATE_ALL; + tty3270_set_timer(tp, 1); } return RAW3270_IO_DONE; } -- cgit v1.1 From ab7373bf235ede864a72f0d2f8f9a21a1748c4a2 Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Tue, 18 Aug 2015 17:42:39 +0200 Subject: s390/ctrlchar: improve handling of magic sysrequests Extract the sysrq handling from the ctrlchar_handle() into a separate function that can be directly used by other users. Introduce a new sysrq_work structure to embed the work_struct and to specify the magic sysrq function to be invoked. Signed-off-by: Hendrik Brueckner Signed-off-by: Martin Schwidefsky --- drivers/s390/char/ctrlchar.c | 16 +++++++++++----- drivers/s390/char/ctrlchar.h | 12 ++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/char/ctrlchar.c b/drivers/s390/char/ctrlchar.c index 8de2deb..f7d9258 100644 --- a/drivers/s390/char/ctrlchar.c +++ b/drivers/s390/char/ctrlchar.c @@ -14,15 +14,21 @@ #include "ctrlchar.h" #ifdef CONFIG_MAGIC_SYSRQ -static int ctrlchar_sysrq_key; +static struct sysrq_work ctrlchar_sysrq; static void ctrlchar_handle_sysrq(struct work_struct *work) { - handle_sysrq(ctrlchar_sysrq_key); + struct sysrq_work *sysrq = container_of(work, struct sysrq_work, work); + + handle_sysrq(sysrq->key); } -static DECLARE_WORK(ctrlchar_work, ctrlchar_handle_sysrq); +void schedule_sysrq_work(struct sysrq_work *sw) +{ + INIT_WORK(&sw->work, ctrlchar_handle_sysrq); + schedule_work(&sw->work); +} #endif @@ -51,8 +57,8 @@ ctrlchar_handle(const unsigned char *buf, int len, struct tty_struct *tty) #ifdef CONFIG_MAGIC_SYSRQ /* racy */ if (len == 3 && buf[1] == '-') { - ctrlchar_sysrq_key = buf[2]; - schedule_work(&ctrlchar_work); + ctrlchar_sysrq.key = buf[2]; + schedule_sysrq_work(&ctrlchar_sysrq); return CTRLCHAR_SYSRQ; } #endif diff --git a/drivers/s390/char/ctrlchar.h b/drivers/s390/char/ctrlchar.h index 1a53552..59c2d6e 100644 --- a/drivers/s390/char/ctrlchar.h +++ b/drivers/s390/char/ctrlchar.h @@ -7,6 +7,8 @@ */ #include +#include +#include extern unsigned int ctrlchar_handle(const unsigned char *buf, int len, struct tty_struct *tty); @@ -17,3 +19,13 @@ ctrlchar_handle(const unsigned char *buf, int len, struct tty_struct *tty); #define CTRLCHAR_SYSRQ (3 << 8) #define CTRLCHAR_MASK (~0xffu) + + +#ifdef CONFIG_MAGIC_SYSRQ +struct sysrq_work { + int key; + struct work_struct work; +}; + +void schedule_sysrq_work(struct sysrq_work *sw); +#endif -- cgit v1.1 From 1d3f9094c6fdb5680fd3b19a5b153308d53a70ee Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Mon, 17 Aug 2015 16:13:41 +0200 Subject: s390/sclp_vt220: support magic sysrequests Implement magic sysrequest handling for the VT220 terminal (also known as the Integrated ASCII console on the HMC/SE). To invoke a "magic sysrequest" function, press "Ctrl+o" followed by a second character that designates the debugging function. The handling of the sysrq is scheduled away from the SCLP IRQ context; because large amounts of sysrq output might fill up the console buffers. The console might deadlock because it cannot empty the buffers while still in the receiving IRQ context. This behavior is the same as for the SCLP console. Reported-by: Horst Weber Signed-off-by: Hendrik Brueckner Reviewed-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/char/sclp_vt220.c | 52 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index ae67386..68d6ee7 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include "sclp.h" +#include "ctrlchar.h" #define SCLP_VT220_MAJOR TTY_MAJOR #define SCLP_VT220_MINOR 65 @@ -477,6 +479,53 @@ sclp_vt220_write(struct tty_struct *tty, const unsigned char *buf, int count) #define SCLP_VT220_SESSION_STARTED 0x80 #define SCLP_VT220_SESSION_DATA 0x00 +#ifdef CONFIG_MAGIC_SYSRQ + +static int sysrq_pressed; +static struct sysrq_work sysrq; + +static void sclp_vt220_reset_session(void) +{ + sysrq_pressed = 0; +} + +static void sclp_vt220_handle_input(const char *buffer, unsigned int count) +{ + int i; + + for (i = 0; i < count; i++) { + /* Handle magic sys request */ + if (buffer[i] == ('O' ^ 0100)) { /* CTRL-O */ + /* + * If pressed again, reset sysrq_pressed + * and flip CTRL-O character + */ + sysrq_pressed = !sysrq_pressed; + if (sysrq_pressed) + continue; + } else if (sysrq_pressed) { + sysrq.key = buffer[i]; + schedule_sysrq_work(&sysrq); + sysrq_pressed = 0; + continue; + } + tty_insert_flip_char(&sclp_vt220_port, buffer[i], 0); + } +} + +#else + +static void sclp_vt220_reset_session(void) +{ +} + +static void sclp_vt220_handle_input(const char *buffer, unsigned int count) +{ + tty_insert_flip_string(&sclp_vt220_port, buffer, count); +} + +#endif + /* * Called by the SCLP to report incoming event buffers. */ @@ -492,12 +541,13 @@ sclp_vt220_receiver_fn(struct evbuf_header *evbuf) switch (*buffer) { case SCLP_VT220_SESSION_ENDED: case SCLP_VT220_SESSION_STARTED: + sclp_vt220_reset_session(); break; case SCLP_VT220_SESSION_DATA: /* Send input to line discipline */ buffer++; count--; - tty_insert_flip_string(&sclp_vt220_port, buffer, count); + sclp_vt220_handle_input(buffer, count); tty_flip_buffer_push(&sclp_vt220_port); break; } -- cgit v1.1