From c9475369bd2bce788796aa313036faecb9725194 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Thu, 12 Jan 2017 15:48:32 +0100 Subject: s390/qeth: rework RX/TX checksum offload Rework the RX/TX checksum offloading command sequence to use the provided function call back mechanims to return card data to the device driver. Signed-off-by: Thomas Richter Reviewed-by: Julian Wiedmann Reviewed-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 2 - drivers/s390/net/qeth_core_main.c | 96 ++++++++++++++++++++++++++------------- drivers/s390/net/qeth_core_mpc.h | 7 +++ 3 files changed, 72 insertions(+), 33 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 6d4b68c483f3..41e46654e591 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -674,8 +674,6 @@ struct qeth_card_info { int broadcast_capable; int unique_id; struct qeth_card_blkt blkt; - __u32 csum_mask; - __u32 tx_csum_mask; enum qeth_ipa_promisc_modes promisc_mode; __u32 diagass_support; __u32 hwtrap; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index e33558313834..5ab80ea0e099 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5289,18 +5289,6 @@ int qeth_setassparms_cb(struct qeth_card *card, if (cmd->hdr.prot_version == QETH_PROT_IPV6) card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled; } - if (cmd->data.setassparms.hdr.assist_no == IPA_INBOUND_CHECKSUM && - cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) { - card->info.csum_mask = cmd->data.setassparms.data.flags_32bit; - QETH_CARD_TEXT_(card, 3, "csum:%d", card->info.csum_mask); - } - if (cmd->data.setassparms.hdr.assist_no == IPA_OUTBOUND_CHECKSUM && - cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) { - card->info.tx_csum_mask = - cmd->data.setassparms.data.flags_32bit; - QETH_CARD_TEXT_(card, 3, "tcsu:%d", card->info.tx_csum_mask); - } - return 0; } EXPORT_SYMBOL_GPL(qeth_setassparms_cb); @@ -6060,23 +6048,78 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev, } EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_settings); +/* Callback to handle checksum offload command reply from OSA card. + * Verify that required features have been enabled on the card. + * Return error in hdr->return_code as this value is checked by caller. + * + * Always returns zero to indicate no further messages from the OSA card. + */ +static int qeth_ipa_checksum_run_cmd_cb(struct qeth_card *card, + struct qeth_reply *reply, + unsigned long data) +{ + struct qeth_ipa_cmd *cmd = (struct qeth_ipa_cmd *) data; + struct qeth_checksum_cmd *chksum_cb = + (struct qeth_checksum_cmd *)reply->param; + + QETH_CARD_TEXT(card, 4, "chkdoccb"); + if (cmd->hdr.return_code) + return 0; + + memset(chksum_cb, 0, sizeof(*chksum_cb)); + if (cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_START) { + chksum_cb->supported = + cmd->data.setassparms.data.chksum.supported; + QETH_CARD_TEXT_(card, 3, "strt:%x", chksum_cb->supported); + } + if (cmd->data.setassparms.hdr.command_code == IPA_CMD_ASS_ENABLE) { + chksum_cb->supported = + cmd->data.setassparms.data.chksum.supported; + chksum_cb->enabled = + cmd->data.setassparms.data.chksum.enabled; + QETH_CARD_TEXT_(card, 3, "supp:%x", chksum_cb->supported); + QETH_CARD_TEXT_(card, 3, "enab:%x", chksum_cb->enabled); + } + return 0; +} + +/* Send command to OSA card and check results. */ +static int qeth_ipa_checksum_run_cmd(struct qeth_card *card, + enum qeth_ipa_funcs ipa_func, + __u16 cmd_code, long data, + struct qeth_checksum_cmd *chksum_cb) +{ + struct qeth_cmd_buffer *iob; + int rc = -ENOMEM; + + QETH_CARD_TEXT(card, 4, "chkdocmd"); + iob = qeth_get_setassparms_cmd(card, ipa_func, cmd_code, + sizeof(__u32), QETH_PROT_IPV4); + if (iob) + rc = qeth_send_setassparms(card, iob, sizeof(__u32), data, + qeth_ipa_checksum_run_cmd_cb, + chksum_cb); + return rc; +} + static int qeth_send_checksum_on(struct qeth_card *card, int cstype) { - long rxtx_arg; + struct qeth_checksum_cmd chksum_cb; int rc; - rc = qeth_send_simple_setassparms(card, cstype, IPA_CMD_ASS_START, 0); + rc = qeth_ipa_checksum_run_cmd(card, cstype, IPA_CMD_ASS_START, 0, + &chksum_cb); if (rc) { + qeth_send_simple_setassparms(card, cstype, IPA_CMD_ASS_STOP, 0); dev_warn(&card->gdev->dev, "Starting HW checksumming for %s failed, using SW checksumming\n", QETH_CARD_IFNAME(card)); return rc; } - rxtx_arg = (cstype == IPA_OUTBOUND_CHECKSUM) ? card->info.tx_csum_mask - : card->info.csum_mask; - rc = qeth_send_simple_setassparms(card, cstype, IPA_CMD_ASS_ENABLE, - rxtx_arg); + rc = qeth_ipa_checksum_run_cmd(card, cstype, IPA_CMD_ASS_ENABLE, + chksum_cb.supported, &chksum_cb); if (rc) { + qeth_send_simple_setassparms(card, cstype, IPA_CMD_ASS_STOP, 0); dev_warn(&card->gdev->dev, "Enabling HW checksumming for %s failed, using SW checksumming\n", QETH_CARD_IFNAME(card)); @@ -6090,19 +6133,10 @@ static int qeth_send_checksum_on(struct qeth_card *card, int cstype) static int qeth_set_ipa_csum(struct qeth_card *card, int on, int cstype) { - int rc; - - if (on) { - rc = qeth_send_checksum_on(card, cstype); - if (rc) - return -EIO; - } else { - rc = qeth_send_simple_setassparms(card, cstype, - IPA_CMD_ASS_STOP, 0); - if (rc) - return -EIO; - } - return 0; + int rc = (on) ? qeth_send_checksum_on(card, cstype) + : qeth_send_simple_setassparms(card, cstype, + IPA_CMD_ASS_STOP, 0); + return rc ? -EIO : 0; } static int qeth_set_ipa_tso(struct qeth_card *card, int on) diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index 6cccc9a49ede..f54ea7224b89 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -352,11 +352,18 @@ struct qeth_arp_query_info { char *udata; }; +/* IPA Assist checksum offload reply layout. */ +struct qeth_checksum_cmd { + __u32 supported; + __u32 enabled; +} __packed; + /* SETASSPARMS IPA Command: */ struct qeth_ipacmd_setassparms { struct qeth_ipacmd_setassparms_hdr hdr; union { __u32 flags_32bit; + struct qeth_checksum_cmd chksum; struct qeth_arp_cache_entry add_arp_entry; struct qeth_arp_query_data query_arp; __u8 ip[16]; -- cgit v1.2.3 From f9d8e6dc0fdee06e3eaf779a52530f4b8be6966f Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Thu, 12 Jan 2017 15:48:33 +0100 Subject: s390/qeth: test RX/TX checksum offload reply Turning on receive and/or transmit checksum offload support on the OSA card requires 2 commands: 1. start command which replies with available features 2. enable command to turn on selected features. The current version does not check the reply of the start command and simply uses the returned value to enable offload features. When the start command returns zero, this leads to a situation where no checksum offload is turned on by the hardware. Even worse no error indication is returned. The Linux kernel assumes the OSA card performs RX/TX checksum offload, but the hardware does not perform any checksum verification at all. This patch checks the return of the start and enable command responses from the hardware and turns off checksum offloading if the commands fails or does not respond with the correct bit setting. Signed-off-by: Thomas Richter Reviewed-by: Julian Wiedmann Reviewed-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 13 +++++++++++++ drivers/s390/net/qeth_core_mpc.h | 10 ++++++++++ 2 files changed, 23 insertions(+) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 5ab80ea0e099..49b813f8f91b 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -6104,11 +6104,19 @@ static int qeth_ipa_checksum_run_cmd(struct qeth_card *card, static int qeth_send_checksum_on(struct qeth_card *card, int cstype) { + const __u32 required_features = QETH_IPA_CHECKSUM_IP_HDR | + QETH_IPA_CHECKSUM_UDP | + QETH_IPA_CHECKSUM_TCP; struct qeth_checksum_cmd chksum_cb; int rc; rc = qeth_ipa_checksum_run_cmd(card, cstype, IPA_CMD_ASS_START, 0, &chksum_cb); + if (!rc) { + if ((required_features & chksum_cb.supported) != + required_features) + rc = -EIO; + } if (rc) { qeth_send_simple_setassparms(card, cstype, IPA_CMD_ASS_STOP, 0); dev_warn(&card->gdev->dev, @@ -6118,6 +6126,11 @@ static int qeth_send_checksum_on(struct qeth_card *card, int cstype) } rc = qeth_ipa_checksum_run_cmd(card, cstype, IPA_CMD_ASS_ENABLE, chksum_cb.supported, &chksum_cb); + if (!rc) { + if ((required_features & chksum_cb.enabled) != + required_features) + rc = -EIO; + } if (rc) { qeth_send_simple_setassparms(card, cstype, IPA_CMD_ASS_STOP, 0); dev_warn(&card->gdev->dev, diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index f54ea7224b89..bc69d0a338ad 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -352,6 +352,16 @@ struct qeth_arp_query_info { char *udata; }; +/* IPA set assist segmentation bit definitions for receive and + * transmit checksum offloading. + */ +enum qeth_ipa_checksum_bits { + QETH_IPA_CHECKSUM_IP_HDR = 0x0002, + QETH_IPA_CHECKSUM_UDP = 0x0008, + QETH_IPA_CHECKSUM_TCP = 0x0010, + QETH_IPA_CHECKSUM_LP2LP = 0x0020 +}; + /* IPA Assist checksum offload reply layout. */ struct qeth_checksum_cmd { __u32 supported; -- cgit v1.2.3 From dae84c8e2a88fab45ff943675410b6c9c0d96a15 Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Thu, 12 Jan 2017 15:48:34 +0100 Subject: s390/qeth: display warning for OSA3 RX/TX checksum offloading When RX/TX checksum offloading is turned on and the adapter is an OSA 3 card in layer 3 mode, the checksum offloading is only performed when both peers use different adapters. If both peers share an OSA 3 card, communication is a memory copy and checksum offloading is not performed. This patch adds a warning to inform the administrator. OSA 3 in layer 2 mode does not offer the RX/TX checksum offload feature. Signed-off-by: Thomas Richter Reviewed-by: Julian Wiedmann Reviewed-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 49b813f8f91b..ca8309ff3ad4 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -6116,6 +6116,11 @@ static int qeth_send_checksum_on(struct qeth_card *card, int cstype) if ((required_features & chksum_cb.supported) != required_features) rc = -EIO; + else if (!(QETH_IPA_CHECKSUM_LP2LP & chksum_cb.supported) && + cstype == IPA_INBOUND_CHECKSUM) + dev_warn(&card->gdev->dev, + "Hardware checksumming is performed only if %s and its peer use different OSA Express 3 ports\n", + QETH_CARD_IFNAME(card)); } if (rc) { qeth_send_simple_setassparms(card, cstype, IPA_CMD_ASS_STOP, 0); -- cgit v1.2.3 From dadc08c7e01907be1116293aa72641a5f560ea4a Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 12 Jan 2017 15:48:35 +0100 Subject: s390/qeth: Allow reading hsuid in state DOWN Accessing the current hsuid via card->options.hsuid is perfectly fine, even when the card is DOWN. Signed-off-by: Julian Wiedmann Reviewed-by: Thomas Richter Acked-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3_sys.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index 0e00a5ce0f00..3cd4d9f7d5bd 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -250,9 +250,6 @@ static ssize_t qeth_l3_dev_hsuid_show(struct device *dev, if (card->info.type != QETH_CARD_TYPE_IQD) return -EPERM; - if (card->state == CARD_STATE_DOWN) - return -EPERM; - memcpy(tmp_hsuid, card->options.hsuid, sizeof(tmp_hsuid)); EBCASC(tmp_hsuid, 8); return sprintf(buf, "%s\n", tmp_hsuid); -- cgit v1.2.3 From c2a7ee2a3beebc1d870c6ba20acf94383d7978f9 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 12 Jan 2017 15:48:36 +0100 Subject: s390/qeth: Remove QETH_IP_HEADER_SIZE Remove unused define QETH_IP_HEADER_SIZE. Signed-off-by: Julian Wiedmann Reviewed-by: Thomas Richter Acked-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 41e46654e591..774ae51569cb 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -281,8 +281,6 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa, #define QETH_HIGH_WATERMARK_PACK 5 #define QETH_WATERMARK_PACK_FUZZ 1 -#define QETH_IP_HEADER_SIZE 40 - /* large receive scatter gather copy break */ #define QETH_RX_SG_CB (PAGE_SIZE >> 1) #define QETH_RX_PULL_LEN 256 -- cgit v1.2.3 From c07cbf2e209198526e059b9e6bb538b18875a19d Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 12 Jan 2017 15:48:37 +0100 Subject: s390/qeth: drop qeth_l2_del_all_macs() parameter The only caller passes del = 0, so remove both the parameter and the code that handles != 0. Signed-off-by: Julian Wiedmann Reviewed-by: Thomas Richter Acked-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l2_main.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 9c921c2833f1..3025f56319e2 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -216,7 +216,7 @@ static int qeth_l2_write_mac(struct qeth_card *card, struct qeth_mac *mac) return rc; } -static void qeth_l2_del_all_macs(struct qeth_card *card, int del) +static void qeth_l2_del_all_macs(struct qeth_card *card) { struct qeth_mac *mac; struct hlist_node *tmp; @@ -224,13 +224,6 @@ static void qeth_l2_del_all_macs(struct qeth_card *card, int del) spin_lock_bh(&card->mclock); hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) { - if (del) { - if (mac->is_uc) - qeth_l2_send_setdelmac(card, mac->mac_addr, - IPA_CMD_DELVMAC); - else - qeth_l2_send_delgroupmac(card, mac->mac_addr); - } hash_del(&mac->hnode); kfree(mac); } @@ -425,7 +418,7 @@ static void qeth_l2_stop_card(struct qeth_card *card, int recovery_mode) card->state = CARD_STATE_SOFTSETUP; } if (card->state == CARD_STATE_SOFTSETUP) { - qeth_l2_del_all_macs(card, 0); + qeth_l2_del_all_macs(card); qeth_clear_ipacmd_list(card); card->state = CARD_STATE_HARDSETUP; } -- cgit v1.2.3 From 4b764d1de395090662fd0291968d5ded523f07e4 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 12 Jan 2017 15:48:38 +0100 Subject: s390/qeth: don't convert return code twice qeth_l2_send_groupmac() already translates the return code, so calling qeth_setdel_makerc() a second time only produces garbage. Signed-off-by: Julian Wiedmann Reviewed-by: Thomas Richter Reviewed-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l2_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 3025f56319e2..38fae10b3479 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -210,8 +210,7 @@ static int qeth_l2_write_mac(struct qeth_card *card, struct qeth_mac *mac) qeth_l2_send_setdelmac(card, mac->mac_addr, IPA_CMD_SETVMAC)); } else { - rc = qeth_setdel_makerc(card, - qeth_l2_send_setgroupmac(card, mac->mac_addr)); + rc = qeth_l2_send_setgroupmac(card, mac->mac_addr); } return rc; } -- cgit v1.2.3 From 754e0b8d92e5ba24a711d51b5fdbbd211e2fdd24 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 12 Jan 2017 15:48:39 +0100 Subject: s390/qeth: consolidate errno translation Consolidate errno handling for MAC management: Instead of doing this in every caller, do it in one place. Signed-off-by: Julian Wiedmann Reviewed-by: Thomas Richter Suggested-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l2_main.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 38fae10b3479..074fc62649e2 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -170,8 +170,7 @@ static int qeth_l2_send_setgroupmac(struct qeth_card *card, __u8 *mac) int rc; QETH_CARD_TEXT(card, 2, "L2Sgmac"); - rc = qeth_setdel_makerc(card, qeth_l2_send_setdelmac(card, mac, - IPA_CMD_SETGMAC)); + rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETGMAC); if (rc == -EEXIST) QETH_DBF_MESSAGE(2, "Group MAC %pM already existing on %s\n", mac, QETH_CARD_IFNAME(card)); @@ -186,8 +185,7 @@ static int qeth_l2_send_delgroupmac(struct qeth_card *card, __u8 *mac) int rc; QETH_CARD_TEXT(card, 2, "L2Dgmac"); - rc = qeth_setdel_makerc(card, qeth_l2_send_setdelmac(card, mac, - IPA_CMD_DELGMAC)); + rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_DELGMAC); if (rc) QETH_DBF_MESSAGE(2, "Could not delete group MAC %pM on %s: %d\n", @@ -206,9 +204,8 @@ static int qeth_l2_write_mac(struct qeth_card *card, struct qeth_mac *mac) int rc; if (mac->is_uc) { - rc = qeth_setdel_makerc(card, - qeth_l2_send_setdelmac(card, mac->mac_addr, - IPA_CMD_SETVMAC)); + rc = qeth_l2_send_setdelmac(card, mac->mac_addr, + IPA_CMD_SETVMAC); } else { rc = qeth_l2_send_setgroupmac(card, mac->mac_addr); } @@ -582,7 +579,8 @@ static int qeth_l2_send_setdelmac(struct qeth_card *card, __u8 *mac, cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); cmd->data.setdelmac.mac_length = OSA_ADDR_LEN; memcpy(&cmd->data.setdelmac.mac, mac, OSA_ADDR_LEN); - return qeth_send_ipa_cmd(card, iob, NULL, NULL); + return qeth_setdel_makerc(card, qeth_send_ipa_cmd(card, iob, + NULL, NULL)); } static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac) @@ -590,8 +588,7 @@ static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac) int rc; QETH_CARD_TEXT(card, 2, "L2Setmac"); - rc = qeth_setdel_makerc(card, qeth_l2_send_setdelmac(card, mac, - IPA_CMD_SETVMAC)); + rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC); if (rc == 0) { card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED; memcpy(card->dev->dev_addr, mac, OSA_ADDR_LEN); @@ -621,8 +618,7 @@ static int qeth_l2_send_delmac(struct qeth_card *card, __u8 *mac) QETH_CARD_TEXT(card, 2, "L2Delmac"); if (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED)) return 0; - rc = qeth_setdel_makerc(card, qeth_l2_send_setdelmac(card, mac, - IPA_CMD_DELVMAC)); + rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_DELVMAC); if (rc == 0) card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED; return rc; -- cgit v1.2.3 From 979d79292af327a12e913ef17f29b85428d0ba0f Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 12 Jan 2017 15:48:40 +0100 Subject: s390/qeth: extract qeth_l2_remove_mac() This matches qeth_l2_write_mac(). Signed-off-by: Julian Wiedmann Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l2_main.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 074fc62649e2..d456740904ef 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -200,16 +200,22 @@ static inline u32 qeth_l2_mac_hash(const u8 *addr) static int qeth_l2_write_mac(struct qeth_card *card, struct qeth_mac *mac) { - - int rc; - if (mac->is_uc) { - rc = qeth_l2_send_setdelmac(card, mac->mac_addr, + return qeth_l2_send_setdelmac(card, mac->mac_addr, IPA_CMD_SETVMAC); } else { - rc = qeth_l2_send_setgroupmac(card, mac->mac_addr); + return qeth_l2_send_setgroupmac(card, mac->mac_addr); + } +} + +static int qeth_l2_remove_mac(struct qeth_card *card, struct qeth_mac *mac) +{ + if (mac->is_uc) { + return qeth_l2_send_setdelmac(card, mac->mac_addr, + IPA_CMD_DELVMAC); + } else { + return qeth_l2_send_delgroupmac(card, mac->mac_addr); } - return rc; } static void qeth_l2_del_all_macs(struct qeth_card *card) @@ -782,14 +788,7 @@ static void qeth_l2_set_rx_mode(struct net_device *dev) hash_for_each_safe(card->mac_htable, i, tmp, mac, hnode) { if (mac->disp_flag == QETH_DISP_ADDR_DELETE) { - if (!mac->is_uc) - rc = qeth_l2_send_delgroupmac(card, - mac->mac_addr); - else { - rc = qeth_l2_send_setdelmac(card, mac->mac_addr, - IPA_CMD_DELVMAC); - } - + qeth_l2_remove_mac(card, mac); hash_del(&mac->hnode); kfree(mac); -- cgit v1.2.3 From ac988d78dc998ec1ce909d2f2ddf0f81e88e54d5 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 12 Jan 2017 15:48:41 +0100 Subject: s390/qeth: shuffle MAC management functions around Move all MAC utility functions in one place, and drop the forward declarations. Signed-off-by: Julian Wiedmann Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l2_main.c | 129 ++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 66 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index d456740904ef..c298759c30a8 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -27,9 +27,6 @@ static int qeth_l2_set_offline(struct ccwgroup_device *); static int qeth_l2_stop(struct net_device *); -static int qeth_l2_send_delmac(struct qeth_card *, __u8 *); -static int qeth_l2_send_setdelmac(struct qeth_card *, __u8 *, - enum qeth_ipa_cmds); static void qeth_l2_set_rx_mode(struct net_device *); static int qeth_l2_recover(void *); static void qeth_bridgeport_query_support(struct qeth_card *card); @@ -165,6 +162,64 @@ static int qeth_setdel_makerc(struct qeth_card *card, int retcode) return rc; } +static int qeth_l2_send_setdelmac(struct qeth_card *card, __u8 *mac, + enum qeth_ipa_cmds ipacmd) +{ + struct qeth_ipa_cmd *cmd; + struct qeth_cmd_buffer *iob; + + QETH_CARD_TEXT(card, 2, "L2sdmac"); + iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4); + if (!iob) + return -ENOMEM; + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + cmd->data.setdelmac.mac_length = OSA_ADDR_LEN; + memcpy(&cmd->data.setdelmac.mac, mac, OSA_ADDR_LEN); + return qeth_setdel_makerc(card, qeth_send_ipa_cmd(card, iob, + NULL, NULL)); +} + +static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac) +{ + int rc; + + QETH_CARD_TEXT(card, 2, "L2Setmac"); + rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC); + if (rc == 0) { + card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED; + memcpy(card->dev->dev_addr, mac, OSA_ADDR_LEN); + dev_info(&card->gdev->dev, + "MAC address %pM successfully registered on device %s\n", + card->dev->dev_addr, card->dev->name); + } else { + card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED; + switch (rc) { + case -EEXIST: + dev_warn(&card->gdev->dev, + "MAC address %pM already exists\n", mac); + break; + case -EPERM: + dev_warn(&card->gdev->dev, + "MAC address %pM is not authorized\n", mac); + break; + } + } + return rc; +} + +static int qeth_l2_send_delmac(struct qeth_card *card, __u8 *mac) +{ + int rc; + + QETH_CARD_TEXT(card, 2, "L2Delmac"); + if (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED)) + return 0; + rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_DELVMAC); + if (rc == 0) + card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED; + return rc; +} + static int qeth_l2_send_setgroupmac(struct qeth_card *card, __u8 *mac) { int rc; @@ -193,11 +248,6 @@ static int qeth_l2_send_delgroupmac(struct qeth_card *card, __u8 *mac) return rc; } -static inline u32 qeth_l2_mac_hash(const u8 *addr) -{ - return get_unaligned((u32 *)(&addr[2])); -} - static int qeth_l2_write_mac(struct qeth_card *card, struct qeth_mac *mac) { if (mac->is_uc) { @@ -232,6 +282,11 @@ static void qeth_l2_del_all_macs(struct qeth_card *card) spin_unlock_bh(&card->mclock); } +static inline u32 qeth_l2_mac_hash(const u8 *addr) +{ + return get_unaligned((u32 *)(&addr[2])); +} + static inline int qeth_l2_get_cast_type(struct qeth_card *card, struct sk_buff *skb) { @@ -572,64 +627,6 @@ out: return work_done; } -static int qeth_l2_send_setdelmac(struct qeth_card *card, __u8 *mac, - enum qeth_ipa_cmds ipacmd) -{ - struct qeth_ipa_cmd *cmd; - struct qeth_cmd_buffer *iob; - - QETH_CARD_TEXT(card, 2, "L2sdmac"); - iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4); - if (!iob) - return -ENOMEM; - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); - cmd->data.setdelmac.mac_length = OSA_ADDR_LEN; - memcpy(&cmd->data.setdelmac.mac, mac, OSA_ADDR_LEN); - return qeth_setdel_makerc(card, qeth_send_ipa_cmd(card, iob, - NULL, NULL)); -} - -static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac) -{ - int rc; - - QETH_CARD_TEXT(card, 2, "L2Setmac"); - rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC); - if (rc == 0) { - card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED; - memcpy(card->dev->dev_addr, mac, OSA_ADDR_LEN); - dev_info(&card->gdev->dev, - "MAC address %pM successfully registered on device %s\n", - card->dev->dev_addr, card->dev->name); - } else { - card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED; - switch (rc) { - case -EEXIST: - dev_warn(&card->gdev->dev, - "MAC address %pM already exists\n", mac); - break; - case -EPERM: - dev_warn(&card->gdev->dev, - "MAC address %pM is not authorized\n", mac); - break; - } - } - return rc; -} - -static int qeth_l2_send_delmac(struct qeth_card *card, __u8 *mac) -{ - int rc; - - QETH_CARD_TEXT(card, 2, "L2Delmac"); - if (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED)) - return 0; - rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_DELVMAC); - if (rc == 0) - card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED; - return rc; -} - static int qeth_l2_request_initial_mac(struct qeth_card *card) { int rc = 0; -- cgit v1.2.3 From 1034051045d125579ab1e8fcd5a724eeb0e70149 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 12 Jan 2017 15:48:42 +0100 Subject: s390/qeth: issue STARTLAN as first IPA command STARTLAN needs to be the first IPA command after MPC initialization completes. So move the qeth_send_startlan() call from the layer disciplines into the core path, right after the MPC handshake. While at it, replace the magic LAN OFFLINE return code with the existing enum. Signed-off-by: Julian Wiedmann Reviewed-by: Thomas Richter Reviewed-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 1 - drivers/s390/net/qeth_core_main.c | 21 +++++++++++++++++---- drivers/s390/net/qeth_l2_main.c | 15 --------------- drivers/s390/net/qeth_l3_main.c | 15 --------------- 4 files changed, 17 insertions(+), 35 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 774ae51569cb..e7addea8741b 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -913,7 +913,6 @@ void qeth_clear_thread_running_bit(struct qeth_card *, unsigned long); int qeth_core_hardsetup_card(struct qeth_card *); void qeth_print_status_message(struct qeth_card *); int qeth_init_qdio_queues(struct qeth_card *); -int qeth_send_startlan(struct qeth_card *); int qeth_send_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *, int (*reply_cb) (struct qeth_card *, struct qeth_reply *, unsigned long), diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index ca8309ff3ad4..315d8a2db7c0 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -2944,7 +2944,7 @@ int qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob, } EXPORT_SYMBOL_GPL(qeth_send_ipa_cmd); -int qeth_send_startlan(struct qeth_card *card) +static int qeth_send_startlan(struct qeth_card *card) { int rc; struct qeth_cmd_buffer *iob; @@ -2957,7 +2957,6 @@ int qeth_send_startlan(struct qeth_card *card) rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); return rc; } -EXPORT_SYMBOL_GPL(qeth_send_startlan); static int qeth_default_setadapterparms_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) @@ -5087,6 +5086,20 @@ retriable: goto out; } + rc = qeth_send_startlan(card); + if (rc) { + QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); + if (rc == IPA_RC_LAN_OFFLINE) { + dev_warn(&card->gdev->dev, + "The LAN is offline\n"); + card->lan_online = 0; + } else { + rc = -ENODEV; + goto out; + } + } else + card->lan_online = 1; + card->options.ipa4.supported_funcs = 0; card->options.ipa6.supported_funcs = 0; card->options.adp.supported_funcs = 0; @@ -5098,14 +5111,14 @@ retriable: if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) { rc = qeth_query_setadapterparms(card); if (rc < 0) { - QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); + QETH_DBF_TEXT_(SETUP, 2, "7err%d", rc); goto out; } } if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) { rc = qeth_query_setdiagass(card); if (rc < 0) { - QETH_DBF_TEXT_(SETUP, 2, "7err%d", rc); + QETH_DBF_TEXT_(SETUP, 2, "8err%d", rc); goto out; } } diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index c298759c30a8..bea483307618 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -1177,21 +1177,6 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) /* softsetup */ QETH_DBF_TEXT(SETUP, 2, "softsetp"); - rc = qeth_send_startlan(card); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - if (rc == 0xe080) { - dev_warn(&card->gdev->dev, - "The LAN is offline\n"); - card->lan_online = 0; - goto contin; - } - rc = -ENODEV; - goto out_remove; - } else - card->lan_online = 1; - -contin: if ((card->info.type == QETH_CARD_TYPE_OSD) || (card->info.type == QETH_CARD_TYPE_OSX)) { rc = qeth_l2_start_ipassists(card); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index ac37d050e765..06d0addcc058 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -3227,21 +3227,6 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) /* softsetup */ QETH_DBF_TEXT(SETUP, 2, "softsetp"); - rc = qeth_send_startlan(card); - if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); - if (rc == 0xe080) { - dev_warn(&card->gdev->dev, - "The LAN is offline\n"); - card->lan_online = 0; - goto contin; - } - rc = -ENODEV; - goto out_remove; - } else - card->lan_online = 1; - -contin: rc = qeth_l3_setadapter_parms(card); if (rc) QETH_DBF_TEXT_(SETUP, 2, "2err%04x", rc); -- cgit v1.2.3 From e48b9eaaa29a0a7d5da2df136b07eefa0180d584 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Thu, 12 Jan 2017 15:48:43 +0100 Subject: s390/qeth: fix retrieval of vipa and proxy-arp addresses qeth devices in layer3 mode need a separate handling of vipa and proxy-arp addresses. vipa and proxy-arp addresses processed by qeth can be read from userspace. Introduced with commit 5f78e29ceebf ("qeth: optimize IP handling in rx_mode callback") the retrieval of vipa and proxy-arp addresses is broken, if more than one vipa or proxy-arp address are set. The qeth code used local variable "int i" for 2 different purposes. This patch now spends 2 separate local variables of type "int". While touching these functions hash_for_each_safe() is converted to hash_for_each(), since there is no removal of hash entries. Signed-off-by: Ursula Braun Reviewed-by: Julian Wiedmann Reference-ID: RQM 3524 Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3_sys.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index 3cd4d9f7d5bd..05e9471e3d3f 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -689,15 +689,15 @@ static ssize_t qeth_l3_dev_vipa_add_show(char *buf, struct qeth_card *card, enum qeth_prot_versions proto) { struct qeth_ipaddr *ipaddr; - struct hlist_node *tmp; char addr_str[40]; + int str_len = 0; int entry_len; /* length of 1 entry string, differs between v4 and v6 */ - int i = 0; + int i; entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; entry_len += 2; /* \n + terminator */ spin_lock_bh(&card->ip_lock); - hash_for_each_safe(card->ip_htable, i, tmp, ipaddr, hnode) { + hash_for_each(card->ip_htable, i, ipaddr, hnode) { if (ipaddr->proto != proto) continue; if (ipaddr->type != QETH_IP_TYPE_VIPA) @@ -705,16 +705,17 @@ static ssize_t qeth_l3_dev_vipa_add_show(char *buf, struct qeth_card *card, /* String must not be longer than PAGE_SIZE. So we check if * string length gets near PAGE_SIZE. Then we can savely display * the next IPv6 address (worst case, compared to IPv4) */ - if ((PAGE_SIZE - i) <= entry_len) + if ((PAGE_SIZE - str_len) <= entry_len) break; qeth_l3_ipaddr_to_string(proto, (const u8 *)&ipaddr->u, addr_str); - i += snprintf(buf + i, PAGE_SIZE - i, "%s\n", addr_str); + str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "%s\n", + addr_str); } spin_unlock_bh(&card->ip_lock); - i += snprintf(buf + i, PAGE_SIZE - i, "\n"); + str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "\n"); - return i; + return str_len; } static ssize_t qeth_l3_dev_vipa_add4_show(struct device *dev, @@ -851,15 +852,15 @@ static ssize_t qeth_l3_dev_rxip_add_show(char *buf, struct qeth_card *card, enum qeth_prot_versions proto) { struct qeth_ipaddr *ipaddr; - struct hlist_node *tmp; char addr_str[40]; + int str_len = 0; int entry_len; /* length of 1 entry string, differs between v4 and v6 */ - int i = 0; + int i; entry_len = (proto == QETH_PROT_IPV4)? 12 : 40; entry_len += 2; /* \n + terminator */ spin_lock_bh(&card->ip_lock); - hash_for_each_safe(card->ip_htable, i, tmp, ipaddr, hnode) { + hash_for_each(card->ip_htable, i, ipaddr, hnode) { if (ipaddr->proto != proto) continue; if (ipaddr->type != QETH_IP_TYPE_RXIP) @@ -867,16 +868,17 @@ static ssize_t qeth_l3_dev_rxip_add_show(char *buf, struct qeth_card *card, /* String must not be longer than PAGE_SIZE. So we check if * string length gets near PAGE_SIZE. Then we can savely display * the next IPv6 address (worst case, compared to IPv4) */ - if ((PAGE_SIZE - i) <= entry_len) + if ((PAGE_SIZE - str_len) <= entry_len) break; qeth_l3_ipaddr_to_string(proto, (const u8 *)&ipaddr->u, addr_str); - i += snprintf(buf + i, PAGE_SIZE - i, "%s\n", addr_str); + str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "%s\n", + addr_str); } spin_unlock_bh(&card->ip_lock); - i += snprintf(buf + i, PAGE_SIZE - i, "\n"); + str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "\n"); - return i; + return str_len; } static ssize_t qeth_l3_dev_rxip_add4_show(struct device *dev, -- cgit v1.2.3 From 9fbd5a0931ed879f9f0a9768086da90aed6328e8 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 15 Dec 2016 11:05:13 +0100 Subject: s390/cio: get rid of variable length array Use a flexible array instead. The size of the structure is not used within chsc_sstpi, therefore no change in semantics but one less sparse warning: drivers/s390/cio/chsc.c:1219:27: warning: Variable length array is used. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 11674698b36d..928d11454b7e 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -1216,7 +1216,7 @@ int chsc_sstpi(void *page, void *result, size_t size) struct chsc_header request; unsigned int rsvd0[3]; struct chsc_header response; - char data[size]; + char data[]; } __attribute__ ((packed)) *rr; int rc; -- cgit v1.2.3 From 227374b1dd08cc052f36768a97a6e3ce628318fc Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 15 Dec 2016 11:28:52 +0100 Subject: s390/zcrypt: make structures static Get rid of these: drivers/s390/crypto/ap_card.c:140:20: warning: symbol 'ap_card_type' was not declared. Should it be static? drivers/s390/crypto/ap_queue.c:567:20: warning: symbol 'ap_queue_type' was not declared. Should it be static? Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_card.c | 2 +- drivers/s390/crypto/ap_queue.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/ap_card.c b/drivers/s390/crypto/ap_card.c index 0110d44172a3..1cd9128593e4 100644 --- a/drivers/s390/crypto/ap_card.c +++ b/drivers/s390/crypto/ap_card.c @@ -137,7 +137,7 @@ static const struct attribute_group *ap_card_dev_attr_groups[] = { NULL }; -struct device_type ap_card_type = { +static struct device_type ap_card_type = { .name = "ap_card", .groups = ap_card_dev_attr_groups, }; diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c index b58a917dc510..7be67fa9f224 100644 --- a/drivers/s390/crypto/ap_queue.c +++ b/drivers/s390/crypto/ap_queue.c @@ -564,7 +564,7 @@ static const struct attribute_group *ap_queue_dev_attr_groups[] = { NULL }; -struct device_type ap_queue_type = { +static struct device_type ap_queue_type = { .name = "ap_queue", .groups = ap_queue_dev_attr_groups, }; -- cgit v1.2.3 From 57c52ae75774c717eb7cd777b5438244ae6b380c Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 15 Dec 2016 12:15:17 +0100 Subject: s390/zcrypt: get rid of variable length arrays The variable length arrays used to specify clobbered memory within ap_nqap and ap_dqap would only work if the length would be known at compile time. This is not the case for both usages. Therefore simply use a full memory clobber and get rid of the old construct. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_asm.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/ap_asm.h b/drivers/s390/crypto/ap_asm.h index 7a630047c372..287b4ad0999e 100644 --- a/drivers/s390/crypto/ap_asm.h +++ b/drivers/s390/crypto/ap_asm.h @@ -129,7 +129,6 @@ static inline struct ap_queue_status ap_nqap(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) { - struct msgblock { char _[length]; }; register unsigned long reg0 asm ("0") = qid | 0x40000000UL; register struct ap_queue_status reg1 asm ("1"); register unsigned long reg2 asm ("2") = (unsigned long) msg; @@ -141,8 +140,8 @@ static inline struct ap_queue_status ap_nqap(ap_qid_t qid, "0: .long 0xb2ad0042\n" /* NQAP */ " brc 2,0b" : "+d" (reg0), "=d" (reg1), "+d" (reg2), "+d" (reg3) - : "d" (reg4), "d" (reg5), "m" (*(struct msgblock *) msg) - : "cc"); + : "d" (reg4), "d" (reg5) + : "cc", "memory"); return reg1; } @@ -168,7 +167,6 @@ static inline struct ap_queue_status ap_dqap(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length) { - struct msgblock { char _[length]; }; register unsigned long reg0 asm("0") = qid | 0x80000000UL; register struct ap_queue_status reg1 asm ("1"); register unsigned long reg2 asm("2") = 0UL; @@ -182,8 +180,8 @@ static inline struct ap_queue_status ap_dqap(ap_qid_t qid, "0: .long 0xb2ae0064\n" /* DQAP */ " brc 6,0b\n" : "+d" (reg0), "=d" (reg1), "+d" (reg2), - "+d" (reg4), "+d" (reg5), "+d" (reg6), "+d" (reg7), - "=m" (*(struct msgblock *) msg) : : "cc"); + "+d" (reg4), "+d" (reg5), "+d" (reg6), "+d" (reg7) + : : "cc", "memory"); *psmid = (((unsigned long long) reg6) << 32) + reg7; return reg1; } -- cgit v1.2.3 From 98cc43ab6bc9574ec4dbc61acccfdcbbfa34d4c5 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 8 Nov 2016 14:28:03 +0100 Subject: s390/cio: clarify cssid usage Currently the cssid in various structures is used as the id of the respective channel subsystem. Sometimes however we call the index in the channel_subsystems array cssid. In some places the id is even used as the index. Provide a new define MAX_CSS_IDX and use it where appropriate. In addition to that provide a dummy function to find a channel subsystem by its id and a macro to iterate over the channel subsystems. Signed-off-by: Sebastian Ott Reviewed-by: Peter Oberparleiter Reviewed-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chp.c | 13 +++++++------ drivers/s390/cio/chp.h | 2 +- drivers/s390/cio/css.c | 30 +++++++++++------------------- drivers/s390/cio/css.h | 10 ++++++++++ 4 files changed, 29 insertions(+), 26 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 876c7e6e3a99..7e0d4f724dda 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -444,6 +444,7 @@ int chp_update_desc(struct channel_path *chp) */ int chp_new(struct chp_id chpid) { + struct channel_subsystem *css = css_by_id(chpid.cssid); struct channel_path *chp; int ret; @@ -456,7 +457,7 @@ int chp_new(struct chp_id chpid) /* fill in status, etc. */ chp->chpid = chpid; chp->state = 1; - chp->dev.parent = &channel_subsystems[chpid.cssid]->device; + chp->dev.parent = &css->device; chp->dev.groups = chp_attr_groups; chp->dev.release = chp_release; mutex_init(&chp->lock); @@ -479,17 +480,17 @@ int chp_new(struct chp_id chpid) put_device(&chp->dev); goto out; } - mutex_lock(&channel_subsystems[chpid.cssid]->mutex); - if (channel_subsystems[chpid.cssid]->cm_enabled) { + mutex_lock(&css->mutex); + if (css->cm_enabled) { ret = chp_add_cmg_attr(chp); if (ret) { device_unregister(&chp->dev); - mutex_unlock(&channel_subsystems[chpid.cssid]->mutex); + mutex_unlock(&css->mutex); goto out; } } - channel_subsystems[chpid.cssid]->chps[chpid.id] = chp; - mutex_unlock(&channel_subsystems[chpid.cssid]->mutex); + css->chps[chpid.id] = chp; + mutex_unlock(&css->mutex); goto out; out_free: kfree(chp); diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h index bb5a68226cda..0d8437b7ea72 100644 --- a/drivers/s390/cio/chp.h +++ b/drivers/s390/cio/chp.h @@ -54,7 +54,7 @@ struct channel_path { /* Return channel_path struct for given chpid. */ static inline struct channel_path *chpid_to_chp(struct chp_id chpid) { - return channel_subsystems[chpid.cssid]->chps[chpid.id]; + return css_by_id(chpid.cssid)->chps[chpid.id]; } int chp_get_status(struct chp_id chpid); diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index bc099b61394d..065c4e1d4cf4 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -36,7 +36,8 @@ int css_init_done = 0; int max_ssid; -struct channel_subsystem *channel_subsystems[__MAX_CSSID + 1]; +#define MAX_CSS_IDX 0 +struct channel_subsystem *channel_subsystems[MAX_CSS_IDX + 1]; static struct bus_type css_bus_type; int @@ -805,13 +806,11 @@ static int css_reboot_event(struct notifier_block *this, unsigned long event, void *ptr) { - int ret, i; + struct channel_subsystem *css; + int ret; ret = NOTIFY_DONE; - for (i = 0; i <= __MAX_CSSID; i++) { - struct channel_subsystem *css; - - css = channel_subsystems[i]; + for_each_css(css) { mutex_lock(&css->mutex); if (css->cm_enabled) if (chsc_secm(css, 0)) @@ -835,16 +834,14 @@ static struct notifier_block css_reboot_notifier = { static int css_power_event(struct notifier_block *this, unsigned long event, void *ptr) { - int ret, i; + struct channel_subsystem *css; + int ret; switch (event) { case PM_HIBERNATION_PREPARE: case PM_SUSPEND_PREPARE: ret = NOTIFY_DONE; - for (i = 0; i <= __MAX_CSSID; i++) { - struct channel_subsystem *css; - - css = channel_subsystems[i]; + for_each_css(css) { mutex_lock(&css->mutex); if (!css->cm_enabled) { mutex_unlock(&css->mutex); @@ -858,10 +855,7 @@ static int css_power_event(struct notifier_block *this, unsigned long event, case PM_POST_HIBERNATION: case PM_POST_SUSPEND: ret = NOTIFY_DONE; - for (i = 0; i <= __MAX_CSSID; i++) { - struct channel_subsystem *css; - - css = channel_subsystems[i]; + for_each_css(css) { mutex_lock(&css->mutex); if (!css->cm_enabled) { mutex_unlock(&css->mutex); @@ -916,7 +910,7 @@ static int __init css_bus_init(void) goto out; /* Setup css structure. */ - for (i = 0; i <= __MAX_CSSID; i++) { + for (i = 0; i <= MAX_CSS_IDX; i++) { struct channel_subsystem *css; css = kmalloc(sizeof(struct channel_subsystem), GFP_KERNEL); @@ -993,10 +987,8 @@ out: static void __init css_bus_cleanup(void) { struct channel_subsystem *css; - int i; - for (i = 0; i <= __MAX_CSSID; i++) { - css = channel_subsystems[i]; + for_each_css(css) { device_unregister(&css->pseudo_subchannel->dev); css->pseudo_subchannel = NULL; if (css_chsc_characteristics.secm) diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 2c9107e20251..cd19c08b93b2 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -130,6 +130,16 @@ struct channel_subsystem { extern struct channel_subsystem *channel_subsystems[]; +/* Dummy helper which needs to change once we support more than one css. */ +static inline struct channel_subsystem *css_by_id(u8 cssid) +{ + return channel_subsystems[0]; +} + +/* Dummy iterator which needs to change once we support more than one css. */ +#define for_each_css(css) \ + for ((css) = channel_subsystems[0]; (css); (css) = NULL) + /* Helper functions to build lists for the slow path. */ void css_schedule_eval(struct subchannel_id schid); void css_schedule_eval_all(void); -- cgit v1.2.3 From e2e0de9b579d4772c2b86e6c9517723ad1e2b22a Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Fri, 17 Jun 2016 19:45:23 +0200 Subject: s390/cio: use cssid for pgid generation Obtain the real channel subsystem id and use that for the generation of a unique path group id. Note that this change does not affect the channel subsystem id as used in the user-visible naming of subchannels and friends. Signed-off-by: Sebastian Ott Reviewed-by: Dong Jia Shi Reviewed-by: Peter Oberparleiter Reviewed-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chsc.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/s390/cio/chsc.h | 2 ++ drivers/s390/cio/css.c | 6 ++++-- drivers/s390/cio/css.h | 2 +- 4 files changed, 53 insertions(+), 3 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 928d11454b7e..7b0b295b2313 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -1131,6 +1131,52 @@ int chsc_enable_facility(int operation_code) return ret; } +int __init chsc_get_cssid(int idx) +{ + struct { + struct chsc_header request; + u8 atype; + u32 : 24; + u32 reserved1[6]; + struct chsc_header response; + u32 reserved2[3]; + struct { + u8 cssid; + u32 : 24; + } list[0]; + } __packed *sdcal_area; + int ret; + + spin_lock_irq(&chsc_page_lock); + memset(chsc_page, 0, PAGE_SIZE); + sdcal_area = chsc_page; + sdcal_area->request.length = 0x0020; + sdcal_area->request.code = 0x0034; + sdcal_area->atype = 4; + + ret = chsc(sdcal_area); + if (ret) { + ret = (ret == 3) ? -ENODEV : -EBUSY; + goto exit; + } + + ret = chsc_error_from_response(sdcal_area->response.code); + if (ret) { + CIO_CRW_EVENT(2, "chsc: sdcal failed (rc=%04x)\n", + sdcal_area->response.code); + goto exit; + } + + if ((addr_t) &sdcal_area->list[idx] < + (addr_t) &sdcal_area->response + sdcal_area->response.length) + ret = sdcal_area->list[idx].cssid; + else + ret = -ENODEV; +exit: + spin_unlock_irq(&chsc_page_lock); + return ret; +} + struct css_general_char css_general_characteristics; struct css_chsc_char css_chsc_characteristics; diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 67c87b6e63ec..321a3f765810 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -242,6 +242,8 @@ int chsc_pnso_brinfo(struct subchannel_id schid, struct chsc_brinfo_resume_token resume_token, int cnc); +int __init chsc_get_cssid(int idx); + #ifdef CONFIG_SCM_BUS int scm_update_information(void); int scm_process_availability_information(void); diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 065c4e1d4cf4..13fea905c61a 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -703,7 +703,8 @@ css_generate_pgid(struct channel_subsystem *css, u32 tod_high) if (css_general_characteristics.mcss) { css->global_pgid.pgid_high.ext_cssid.version = 0x80; - css->global_pgid.pgid_high.ext_cssid.cssid = css->cssid; + css->global_pgid.pgid_high.ext_cssid.cssid = + (css->cssid < 0) ? 0 : css->cssid; } else { css->global_pgid.pgid_high.cpu_addr = stap(); } @@ -794,7 +795,8 @@ static int __init setup_css(int nr) } mutex_init(&css->mutex); css->valid = 1; - css->cssid = nr; + css->cssid = chsc_get_cssid(nr); + dev_set_name(&css->device, "css%x", nr); css->device.release = channel_subsystem_release; tod_high = (u32) (get_tod_clock() >> 32); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index cd19c08b93b2..3056f96f9d82 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -113,7 +113,7 @@ extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *); void css_update_ssd_info(struct subchannel *sch); struct channel_subsystem { - u8 cssid; + int cssid; int valid; struct channel_path *chps[__MAX_CHPID + 1]; struct device device; -- cgit v1.2.3 From 6c7012688bdc8c9349dc8f289d2da2dacba3928d Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 11 Oct 2016 16:37:43 +0200 Subject: s390/cio: css attribute cleanup Cleanup the code to handle the css device attribute. Move everything to an attribute group to let the driver core handle attribute creation and removal. Signed-off-by: Sebastian Ott Reviewed-by: Cornelia Huck Reviewed-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/css.c | 57 +++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 28 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 13fea905c61a..59bd441b1cd3 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -729,28 +729,24 @@ channel_subsystem_release(struct device *dev) kfree(css); } -static ssize_t -css_cm_enable_show(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t cm_enable_show(struct device *dev, struct device_attribute *a, + char *buf) { struct channel_subsystem *css = to_css(dev); int ret; - if (!css) - return 0; mutex_lock(&css->mutex); ret = sprintf(buf, "%x\n", css->cm_enabled); mutex_unlock(&css->mutex); return ret; } -static ssize_t -css_cm_enable_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t cm_enable_store(struct device *dev, struct device_attribute *a, + const char *buf, size_t count) { struct channel_subsystem *css = to_css(dev); - int ret; unsigned long val; + int ret; ret = kstrtoul(buf, 16, &val); if (ret) @@ -769,8 +765,28 @@ css_cm_enable_store(struct device *dev, struct device_attribute *attr, mutex_unlock(&css->mutex); return ret < 0 ? ret : count; } +static DEVICE_ATTR_RW(cm_enable); + +static umode_t cm_enable_mode(struct kobject *kobj, struct attribute *attr, + int index) +{ + return css_chsc_characteristics.secm ? attr->mode : 0; +} + +static struct attribute *cssdev_cm_attrs[] = { + &dev_attr_cm_enable.attr, + NULL, +}; + +static struct attribute_group cssdev_cm_attr_group = { + .attrs = cssdev_cm_attrs, + .is_visible = cm_enable_mode, +}; -static DEVICE_ATTR(cm_enable, 0644, css_cm_enable_show, css_cm_enable_store); +static const struct attribute_group *cssdev_attr_groups[] = { + &cssdev_cm_attr_group, + NULL, +}; static int __init setup_css(int nr) { @@ -798,6 +814,7 @@ static int __init setup_css(int nr) css->cssid = chsc_get_cssid(nr); dev_set_name(&css->device, "css%x", nr); + css->device.groups = cssdev_attr_groups; css->device.release = channel_subsystem_release; tod_high = (u32) (get_tod_clock() >> 32); css_generate_pgid(css, tod_high); @@ -931,16 +948,11 @@ static int __init css_bus_init(void) put_device(&css->device); goto out_unregister; } - if (css_chsc_characteristics.secm) { - ret = device_create_file(&css->device, - &dev_attr_cm_enable); - if (ret) - goto out_device; - } ret = device_register(&css->pseudo_subchannel->dev); if (ret) { put_device(&css->pseudo_subchannel->dev); - goto out_file; + device_unregister(&css->device); + goto out_unregister; } } ret = register_reboot_notifier(&css_reboot_notifier); @@ -957,12 +969,6 @@ static int __init css_bus_init(void) isc_register(IO_SCH_ISC); return 0; -out_file: - if (css_chsc_characteristics.secm) - device_remove_file(&channel_subsystems[i]->device, - &dev_attr_cm_enable); -out_device: - device_unregister(&channel_subsystems[i]->device); out_unregister: while (i > 0) { struct channel_subsystem *css; @@ -971,9 +977,6 @@ out_unregister: css = channel_subsystems[i]; device_unregister(&css->pseudo_subchannel->dev); css->pseudo_subchannel = NULL; - if (css_chsc_characteristics.secm) - device_remove_file(&css->device, - &dev_attr_cm_enable); device_unregister(&css->device); } bus_unregister(&css_bus_type); @@ -993,8 +996,6 @@ static void __init css_bus_cleanup(void) for_each_css(css) { device_unregister(&css->pseudo_subchannel->dev); css->pseudo_subchannel = NULL; - if (css_chsc_characteristics.secm) - device_remove_file(&css->device, &dev_attr_cm_enable); device_unregister(&css->device); } bus_unregister(&css_bus_type); -- cgit v1.2.3 From 15a2044d7f3c1ee16de92a45f035f3abbe1bb885 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 25 Oct 2016 14:05:08 +0200 Subject: s390/cio: css initialization cleanup Simplify error handling during css initialization by moving the error handling code to setup_css (which now cleans up after itself). Also remove the odd special cleanup handling of the pseudo_subchannel. Signed-off-by: Sebastian Ott Reviewed-by: Cornelia Huck Reviewed-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/css.c | 103 ++++++++++++++++++++++--------------------------- 1 file changed, 47 insertions(+), 56 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 59bd441b1cd3..8b608e0d2d26 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -714,18 +714,11 @@ css_generate_pgid(struct channel_subsystem *css, u32 tod_high) css->global_pgid.tod_high = tod_high; } -static void -channel_subsystem_release(struct device *dev) +static void channel_subsystem_release(struct device *dev) { - struct channel_subsystem *css; + struct channel_subsystem *css = to_css(dev); - css = to_css(dev); mutex_destroy(&css->mutex); - if (css->pseudo_subchannel) { - /* Implies that it has been generated but never registered. */ - css_subchannel_release(&css->pseudo_subchannel->dev); - css->pseudo_subchannel = NULL; - } kfree(css); } @@ -790,35 +783,59 @@ static const struct attribute_group *cssdev_attr_groups[] = { static int __init setup_css(int nr) { - u32 tod_high; - int ret; struct channel_subsystem *css; + int ret; - css = channel_subsystems[nr]; - memset(css, 0, sizeof(struct channel_subsystem)); - css->pseudo_subchannel = - kzalloc(sizeof(*css->pseudo_subchannel), GFP_KERNEL); - if (!css->pseudo_subchannel) + css = kzalloc(sizeof(*css), GFP_KERNEL); + if (!css) return -ENOMEM; + + channel_subsystems[nr] = css; + dev_set_name(&css->device, "css%x", nr); + css->device.groups = cssdev_attr_groups; + css->device.release = channel_subsystem_release; + + mutex_init(&css->mutex); + css->valid = 1; + css->cssid = chsc_get_cssid(nr); + css_generate_pgid(css, (u32) (get_tod_clock() >> 32)); + + ret = device_register(&css->device); + if (ret) { + put_device(&css->device); + goto out_err; + } + + css->pseudo_subchannel = kzalloc(sizeof(*css->pseudo_subchannel), + GFP_KERNEL); + if (!css->pseudo_subchannel) { + device_unregister(&css->device); + ret = -ENOMEM; + goto out_err; + } + css->pseudo_subchannel->dev.parent = &css->device; css->pseudo_subchannel->dev.release = css_subchannel_release; - dev_set_name(&css->pseudo_subchannel->dev, "defunct"); mutex_init(&css->pseudo_subchannel->reg_mutex); ret = css_sch_create_locks(css->pseudo_subchannel); if (ret) { kfree(css->pseudo_subchannel); - return ret; + device_unregister(&css->device); + goto out_err; } - mutex_init(&css->mutex); - css->valid = 1; - css->cssid = chsc_get_cssid(nr); - dev_set_name(&css->device, "css%x", nr); - css->device.groups = cssdev_attr_groups; - css->device.release = channel_subsystem_release; - tod_high = (u32) (get_tod_clock() >> 32); - css_generate_pgid(css, tod_high); - return 0; + dev_set_name(&css->pseudo_subchannel->dev, "defunct"); + ret = device_register(&css->pseudo_subchannel->dev); + if (ret) { + put_device(&css->pseudo_subchannel->dev); + device_unregister(&css->device); + goto out_err; + } + + return ret; +out_err: + channel_subsystems[nr] = NULL; + return ret; } static int css_reboot_event(struct notifier_block *this, @@ -930,30 +947,9 @@ static int __init css_bus_init(void) /* Setup css structure. */ for (i = 0; i <= MAX_CSS_IDX; i++) { - struct channel_subsystem *css; - - css = kmalloc(sizeof(struct channel_subsystem), GFP_KERNEL); - if (!css) { - ret = -ENOMEM; - goto out_unregister; - } - channel_subsystems[i] = css; ret = setup_css(i); - if (ret) { - kfree(channel_subsystems[i]); + if (ret) goto out_unregister; - } - ret = device_register(&css->device); - if (ret) { - put_device(&css->device); - goto out_unregister; - } - ret = device_register(&css->pseudo_subchannel->dev); - if (ret) { - put_device(&css->pseudo_subchannel->dev); - device_unregister(&css->device); - goto out_unregister; - } } ret = register_reboot_notifier(&css_reboot_notifier); if (ret) @@ -970,13 +966,9 @@ static int __init css_bus_init(void) return 0; out_unregister: - while (i > 0) { - struct channel_subsystem *css; - - i--; - css = channel_subsystems[i]; + while (i-- > 0) { + struct channel_subsystem *css = channel_subsystems[i]; device_unregister(&css->pseudo_subchannel->dev); - css->pseudo_subchannel = NULL; device_unregister(&css->device); } bus_unregister(&css_bus_type); @@ -995,7 +987,6 @@ static void __init css_bus_cleanup(void) for_each_css(css) { device_unregister(&css->pseudo_subchannel->dev); - css->pseudo_subchannel = NULL; device_unregister(&css->device); } bus_unregister(&css_bus_type); -- cgit v1.2.3 From 64dfdd4b539f0d2e552e8b83aab119c8eafbbc83 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 11 Oct 2016 18:21:36 +0200 Subject: s390/cio: export real cssid Signed-off-by: Sebastian Ott Reviewed-by: Peter Oberparleiter Reviewed-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/css.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 8b608e0d2d26..8faffed61092 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -722,6 +722,18 @@ static void channel_subsystem_release(struct device *dev) kfree(css); } +static ssize_t real_cssid_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct channel_subsystem *css = to_css(dev); + + if (css->cssid < 0) + return -EINVAL; + + return sprintf(buf, "%x\n", css->cssid); +} +static DEVICE_ATTR_RO(real_cssid); + static ssize_t cm_enable_show(struct device *dev, struct device_attribute *a, char *buf) { @@ -766,6 +778,15 @@ static umode_t cm_enable_mode(struct kobject *kobj, struct attribute *attr, return css_chsc_characteristics.secm ? attr->mode : 0; } +static struct attribute *cssdev_attrs[] = { + &dev_attr_real_cssid.attr, + NULL, +}; + +static struct attribute_group cssdev_attr_group = { + .attrs = cssdev_attrs, +}; + static struct attribute *cssdev_cm_attrs[] = { &dev_attr_cm_enable.attr, NULL, @@ -777,6 +798,7 @@ static struct attribute_group cssdev_cm_attr_group = { }; static const struct attribute_group *cssdev_attr_groups[] = { + &cssdev_attr_group, &cssdev_cm_attr_group, NULL, }; -- cgit v1.2.3 From 00851e6925a827362e3b84ead0c0cf3e86a31aec Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 8 Nov 2016 17:50:40 +0100 Subject: s390/cio: remove unused struct member Remove an unused member of struct channel subsystem. Signed-off-by: Sebastian Ott Reviewed-by: Cornelia Huck Reviewed-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/css.c | 1 - drivers/s390/cio/css.h | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 8faffed61092..e2aa944eb566 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -818,7 +818,6 @@ static int __init setup_css(int nr) css->device.release = channel_subsystem_release; mutex_init(&css->mutex); - css->valid = 1; css->cssid = chsc_get_cssid(nr); css_generate_pgid(css, (u32) (get_tod_clock() >> 32)); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 3056f96f9d82..c9f3fb39ebeb 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -114,7 +114,6 @@ void css_update_ssd_info(struct subchannel *sch); struct channel_subsystem { int cssid; - int valid; struct channel_path *chps[__MAX_CHPID + 1]; struct device device; struct pgid global_pgid; -- cgit v1.2.3 From dd695546603e8f984137d4dc806dd12e19b6bc27 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Thu, 15 Dec 2016 15:18:23 +0100 Subject: s390/zcore: remove unneeded linux/miscdevice.h include drivers/s390/char/zcore.c does not contain any miscdevice so the inclusion of linux/miscdevice.h is uncessary. Signed-off-by: Corentin Labbe Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/char/zcore.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index d3b51edb056e..863211a0b312 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -15,7 +15,6 @@ #include #include -#include #include #include -- cgit v1.2.3 From 90b3baa232ea6938ab707e3db2e90609494e6c54 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 20 Dec 2016 16:08:05 +0100 Subject: s390: proper type casts for csum_partial invocations Keep sparse and other static code checkers from emitting warnings like: arch/s390/kernel/ipl.c:1549:14: warning: incorrect type in assignment (different base types) arch/s390/kernel/ipl.c:1549:14: expected unsigned int [unsigned] csum arch/s390/kernel/ipl.c:1549:14: got restricted __wsum All usages in s390 code are ok. Therefore add proper casts. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/ipl.c | 3 ++- arch/s390/kernel/os_info.c | 6 +++--- drivers/s390/char/zcore.c | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/s390') diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index ff3364a067ff..2c6ddced5394 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -1546,7 +1546,8 @@ static void dump_reipl_run(struct shutdown_trigger *trigger) unsigned long ipib = (unsigned long) reipl_block_actual; unsigned int csum; - csum = csum_partial(reipl_block_actual, reipl_block_actual->hdr.len, 0); + csum = (__force unsigned int) + csum_partial(reipl_block_actual, reipl_block_actual->hdr.len, 0); mem_assign_absolute(S390_lowcore.ipib, ipib); mem_assign_absolute(S390_lowcore.ipib_checksum, csum); dump_run(trigger); diff --git a/arch/s390/kernel/os_info.c b/arch/s390/kernel/os_info.c index 87f05e475ae8..753ba63182b9 100644 --- a/arch/s390/kernel/os_info.c +++ b/arch/s390/kernel/os_info.c @@ -26,7 +26,7 @@ static struct os_info os_info __page_aligned_data; u32 os_info_csum(struct os_info *os_info) { int size = sizeof(*os_info) - offsetof(struct os_info, version_major); - return csum_partial(&os_info->version_major, size, 0); + return (__force u32)csum_partial(&os_info->version_major, size, 0); } /* @@ -46,7 +46,7 @@ void os_info_entry_add(int nr, void *ptr, u64 size) { os_info.entry[nr].addr = (u64)(unsigned long)ptr; os_info.entry[nr].size = size; - os_info.entry[nr].csum = csum_partial(ptr, size, 0); + os_info.entry[nr].csum = (__force u32)csum_partial(ptr, size, 0); os_info.csum = os_info_csum(&os_info); } @@ -93,7 +93,7 @@ static void os_info_old_alloc(int nr, int align) msg = "copy failed"; goto fail_free; } - csum = csum_partial(buf_align, size, 0); + csum = (__force u32)csum_partial(buf_align, size, 0); if (csum != os_info_old->entry[nr].csum) { msg = "checksum failed"; goto fail_free; diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 863211a0b312..aaed778f67c4 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -272,7 +272,7 @@ static int __init zcore_reipl_init(void) rc = memcpy_hsa_kernel(ipl_block, ipib_info.ipib, PAGE_SIZE); else rc = memcpy_real(ipl_block, (void *) ipib_info.ipib, PAGE_SIZE); - if (rc || csum_partial(ipl_block, ipl_block->hdr.len, 0) != + if (rc || (__force u32)csum_partial(ipl_block, ipl_block->hdr.len, 0) != ipib_info.checksum) { TRACE("Checksum does not match\n"); free_page((unsigned long) ipl_block); -- cgit v1.2.3 From 970ba6ac6a59ff1f1579e472a97765adc50186f9 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 2 Jan 2017 09:59:40 +0100 Subject: s390: use false/true when using bool Yet another trivial patch to reduce the noise that coccinelle generates. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/processor.c | 2 +- arch/s390/kernel/time.c | 2 +- drivers/s390/char/con3270.c | 2 +- drivers/s390/char/raw3270.c | 2 +- drivers/s390/crypto/zcrypt_api.c | 4 ++-- drivers/s390/virtio/virtio_ccw.c | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/s390') diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c index 9e60ef144d03..8733b07b5691 100644 --- a/arch/s390/kernel/processor.c +++ b/arch/s390/kernel/processor.c @@ -32,7 +32,7 @@ static bool machine_has_cpu_mhz; void __init cpu_detect_mhz_feature(void) { if (test_facility(34) && __ecag(ECAG_CPU_ATTRIBUTE, 0) != -1UL) - machine_has_cpu_mhz = 1; + machine_has_cpu_mhz = true; } static void update_cpu_mhz(void *arg) diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 52949df88529..eebbd6adc6c5 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -492,7 +492,7 @@ static void __init stp_reset(void) pr_warn("The real or virtual hardware system does not provide an STP interface\n"); free_page((unsigned long) stp_page); stp_page = NULL; - stp_online = 0; + stp_online = false; } } diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 285b4006f44b..8522cfce5b4e 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -31,7 +31,7 @@ static struct raw3270_fn con3270_fn; -static bool auto_update = 1; +static bool auto_update = true; module_param(auto_update, bool, 0); /* diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index a2da898ce90f..710f2292911d 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -82,7 +82,7 @@ static LIST_HEAD(raw3270_devices); static int raw3270_registered; /* Module parameters */ -static bool tubxcorrect = 0; +static bool tubxcorrect; module_param(tubxcorrect, bool, 0); /* diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 51eece9af577..919f7aa5a09a 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -201,7 +201,7 @@ static inline bool zcrypt_card_compare(struct zcrypt_card *zc, unsigned weight, unsigned pref_weight) { if (!pref_zc) - return 0; + return false; weight += atomic_read(&zc->load); pref_weight += atomic_read(&pref_zc->load); if (weight == pref_weight) @@ -215,7 +215,7 @@ static inline bool zcrypt_queue_compare(struct zcrypt_queue *zq, unsigned weight, unsigned pref_weight) { if (!pref_zq) - return 0; + return false; weight += atomic_read(&zq->load); pref_weight += atomic_read(&pref_zq->load); if (weight == pref_weight) diff --git a/drivers/s390/virtio/virtio_ccw.c b/drivers/s390/virtio/virtio_ccw.c index 639ed4e6afd1..879a5f63e7d4 100644 --- a/drivers/s390/virtio/virtio_ccw.c +++ b/drivers/s390/virtio/virtio_ccw.c @@ -659,7 +659,7 @@ static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, ret = virtio_ccw_register_adapter_ind(vcdev, vqs, nvqs, ccw); if (ret) /* no error, just fall back to legacy interrupts */ - vcdev->is_thinint = 0; + vcdev->is_thinint = false; } if (!vcdev->is_thinint) { /* Register queue indicators with host. */ -- cgit v1.2.3 From 21665912dd722656dfc4481f82c73397c8f2d07f Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Thu, 12 Jan 2017 18:53:56 +0100 Subject: s390/cio: remove cmf related code relevant for 31 bit only This is a leftover from the 31 bit era to avoid 64 bit divisions. Get rid of it. Reported-by: Heiko Carstens Signed-off-by: Sebastian Ott Acked-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/cmf.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 6b6386e9a500..220491d27ef4 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -1085,15 +1085,9 @@ static ssize_t cmb_show_avg_utilization(struct device *dev, data.function_pending_time + data.device_disconnect_time; - /* shift to avoid long long division */ - while (-1ul < (data.elapsed_time | utilization)) { - utilization >>= 8; - data.elapsed_time >>= 8; - } - /* calculate value in 0.1 percent units */ - t = (unsigned long) data.elapsed_time / 1000; - u = (unsigned long) utilization / t; + t = data.elapsed_time / 1000; + u = utilization / t; return sprintf(buf, "%02ld.%01ld%%\n", u/ 10, u - (u/ 10) * 10); } -- cgit v1.2.3 From 7fbe5c0f2af3ab82fe6880af557e98a10d711370 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Mon, 16 Jan 2017 09:43:29 +0100 Subject: s390/zcrypt: use spin_lock_bh for all queue locks and unlocks. During tests the Kernel complained about inconsistend lock state: inconsistent {IN-SOFTIRQ-W} -> {SOFTIRQ-ON-W} usage. Now all the queue locks use spin_lock_bh/spin_unlock_bh. Signed-off-by: Harald Freudenberger Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/zcrypt_api.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 919f7aa5a09a..926169c3d9b9 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -668,6 +668,7 @@ static void zcrypt_qdepth_mask(char qdepth[AP_DEVICES]) memset(qdepth, 0, sizeof(char) * AP_DEVICES); spin_lock(&zcrypt_list_lock); + local_bh_disable(); for_each_zcrypt_card(zc) { for_each_zcrypt_queue(zq, zc) { if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index) @@ -679,6 +680,7 @@ static void zcrypt_qdepth_mask(char qdepth[AP_DEVICES]) spin_unlock(&zq->queue->lock); } } + local_bh_enable(); spin_unlock(&zcrypt_list_lock); } @@ -689,6 +691,7 @@ static void zcrypt_perdev_reqcnt(int reqcnt[AP_DEVICES]) memset(reqcnt, 0, sizeof(int) * AP_DEVICES); spin_lock(&zcrypt_list_lock); + local_bh_disable(); for_each_zcrypt_card(zc) { for_each_zcrypt_queue(zq, zc) { if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index) @@ -699,6 +702,7 @@ static void zcrypt_perdev_reqcnt(int reqcnt[AP_DEVICES]) spin_unlock(&zq->queue->lock); } } + local_bh_enable(); spin_unlock(&zcrypt_list_lock); } @@ -710,6 +714,7 @@ static int zcrypt_pendingq_count(void) pendingq_count = 0; spin_lock(&zcrypt_list_lock); + local_bh_disable(); for_each_zcrypt_card(zc) { for_each_zcrypt_queue(zq, zc) { if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index) @@ -719,6 +724,7 @@ static int zcrypt_pendingq_count(void) spin_unlock(&zq->queue->lock); } } + local_bh_enable(); spin_unlock(&zcrypt_list_lock); return pendingq_count; } @@ -731,6 +737,7 @@ static int zcrypt_requestq_count(void) requestq_count = 0; spin_lock(&zcrypt_list_lock); + local_bh_disable(); for_each_zcrypt_card(zc) { for_each_zcrypt_queue(zq, zc) { if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index) @@ -740,6 +747,7 @@ static int zcrypt_requestq_count(void) spin_unlock(&zq->queue->lock); } } + local_bh_enable(); spin_unlock(&zcrypt_list_lock); return requestq_count; } -- cgit v1.2.3 From 09762dcb623b5cff08c624250130cdcd389d5044 Mon Sep 17 00:00:00 2001 From: Jan Höppner Date: Mon, 21 Nov 2016 13:21:59 +0100 Subject: s390/dasd: Always store parameter elements in an array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the DASD driver is built into the kernel, the entire comma separated parameter list is stored as one single element in the dasd[] array, opposed to the module build where each element is stored separately in dasd[]. There is no point in doing so. Therefore, store each part of the list as single elements in dasd[] as well when built into the kernel. Also, create a define for the maximum of 256 parameters. Reviewed-by: Stefan Haberland Reviewed-by: Sebastian Ott Signed-off-by: Jan Höppner Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_devmap.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index dd46e96a3034..4c8eff6b1aab 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -26,6 +26,7 @@ /* This is ugly... */ #define PRINTK_HEADER "dasd_devmap:" #define DASD_BUS_ID_SIZE 20 +#define DASD_MAX_PARAMS 256 #include "dasd_int.h" @@ -76,7 +77,7 @@ EXPORT_SYMBOL_GPL(dasd_nofcx); * it is named 'dasd' to directly be filled by insmod with the comma separated * strings when running as a module. */ -static char *dasd[256]; +static char *dasd[DASD_MAX_PARAMS]; module_param_array(dasd, charp, NULL, S_IRUGO); /* @@ -104,18 +105,19 @@ dasd_hash_busid(const char *bus_id) } #ifndef MODULE -/* - * The parameter parsing functions for builtin-drivers are called - * before kmalloc works. Store the pointers to the parameters strings - * into dasd[] for later processing. - */ -static int __init -dasd_call_setup(char *str) +static int __init dasd_call_setup(char *opt) { - static int count = 0; + static int i; + char *tmp; + + while (i < DASD_MAX_PARAMS) { + tmp = strsep(&opt, ","); + if (!tmp) + break; + + dasd[i++] = tmp; + } - if (count < 256) - dasd[count++] = str; return 1; } @@ -364,10 +366,8 @@ dasd_parse_next_element( char *parsestring ) { /* * Parse parameters stored in dasd[] * The 'dasd=...' parameter allows to specify a comma separated list of - * keywords and device ranges. When the dasd driver is build into the kernel, - * the complete list will be stored as one element of the dasd[] array. - * When the dasd driver is build as a module, then the list is broken into - * it's elements and each dasd[] entry contains one element. + * keywords and device ranges. The parameters in that list will be stored as + * separate elementes in dasd[]. */ int dasd_parse(void) @@ -376,7 +376,7 @@ dasd_parse(void) char *parsestring; rc = 0; - for (i = 0; i < 256; i++) { + for (i = 0; i < DASD_MAX_PARAMS; i++) { if (dasd[i] == NULL) break; parsestring = dasd[i]; -- cgit v1.2.3 From 3b1bea012710c1a299573c7a6a0584d623e6cbcf Mon Sep 17 00:00:00 2001 From: Jan Höppner Date: Tue, 22 Nov 2016 18:11:46 +0100 Subject: s390/dasd: Improve parameter list parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function dasd_busid() still uses simple_strtoul() to convert a string to an integer value. This function is obsolete for quite some time already and should be replaced. The whole parameter parsing semantic still relies somewhat on the fact, that simple_strtoul() parses a string containing literals without complains and just returns the parsed integer value plus the residual string. kstrtoint(), however, would return -EINVAL in such a case. Since we want to get rid of simple_strtoul() and now have a nice dasd[] containing only single elements, we can clean up and simplify a few things. Replace simple_strtoul() with kstrtouint(), improve and simplify the overall parameter parsing by the following: - instead of residual strings return proper error codes - remove dasd_parse_next_element() and decide directly what sort of element is being parsed - if we parse a device or a range of devices, split that element into separate bits with a new function - remove warning about invalid ending as it doesn't apply anymore - annotate all parsing functions and data that can be freed after initialisation with __init and __initdata respectively - clean up bits and pieces while at it Reviewed-by: Stefan Haberland Reviewed-by: Sebastian Ott Signed-off-by: Jan Höppner Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_devmap.c | 258 +++++++++++++++++++-------------------- drivers/s390/block/dasd_int.h | 2 +- 2 files changed, 125 insertions(+), 135 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 4c8eff6b1aab..041bdc4429c1 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -107,7 +107,7 @@ dasd_hash_busid(const char *bus_id) #ifndef MODULE static int __init dasd_call_setup(char *opt) { - static int i; + static int i __initdata; char *tmp; while (i < DASD_MAX_PARAMS) { @@ -129,14 +129,13 @@ __setup ("dasd=", dasd_call_setup); /* * Read a device busid/devno from a string. */ -static int - -dasd_busid(char **str, int *id0, int *id1, int *devno) +static int __init dasd_busid(char *str, int *id0, int *id1, int *devno) { - int val, old_style; + unsigned int val; + char *tok; /* Interpret ipldev busid */ - if (strncmp(DASD_IPLDEV, *str, strlen(DASD_IPLDEV)) == 0) { + if (strncmp(DASD_IPLDEV, str, strlen(DASD_IPLDEV)) == 0) { if (ipl_info.type != IPL_TYPE_CCW) { pr_err("The IPL device is not a CCW device\n"); return -EINVAL; @@ -144,63 +143,50 @@ dasd_busid(char **str, int *id0, int *id1, int *devno) *id0 = 0; *id1 = ipl_info.data.ccw.dev_id.ssid; *devno = ipl_info.data.ccw.dev_id.devno; - *str += strlen(DASD_IPLDEV); return 0; } - /* check for leading '0x' */ - old_style = 0; - if ((*str)[0] == '0' && (*str)[1] == 'x') { - *str += 2; - old_style = 1; - } - if (!isxdigit((*str)[0])) /* We require at least one hex digit */ - return -EINVAL; - val = simple_strtoul(*str, str, 16); - if (old_style || (*str)[0] != '.') { + + /* Old style 0xXXXX or XXXX */ + if (!kstrtouint(str, 16, &val)) { *id0 = *id1 = 0; if (val < 0 || val > 0xffff) return -EINVAL; *devno = val; return 0; } + /* New style x.y.z busid */ - if (val < 0 || val > 0xff) + tok = strsep(&str, "."); + if (kstrtouint(tok, 16, &val) || val > 0xff) return -EINVAL; *id0 = val; - (*str)++; - if (!isxdigit((*str)[0])) /* We require at least one hex digit */ - return -EINVAL; - val = simple_strtoul(*str, str, 16); - if (val < 0 || val > 0xff || (*str)++[0] != '.') + + tok = strsep(&str, "."); + if (kstrtouint(tok, 16, &val) || val > 0xff) return -EINVAL; *id1 = val; - if (!isxdigit((*str)[0])) /* We require at least one hex digit */ - return -EINVAL; - val = simple_strtoul(*str, str, 16); - if (val < 0 || val > 0xffff) + + tok = strsep(&str, "."); + if (kstrtouint(tok, 16, &val) || val > 0xffff) return -EINVAL; *devno = val; + return 0; } /* - * Read colon separated list of dasd features. Currently there is - * only one: "ro" for read-only devices. The default feature set - * is empty (value 0). + * Read colon separated list of dasd features. */ -static int -dasd_feature_list(char *str, char **endp) +static int __init dasd_feature_list(char *str) { int features, len, rc; + features = 0; rc = 0; - if (*str != '(') { - *endp = str; + + if (!str) return DASD_FEATURE_DEFAULT; - } - str++; - features = 0; while (1) { for (len = 0; @@ -225,15 +211,8 @@ dasd_feature_list(char *str, char **endp) break; str++; } - if (*str != ')') { - pr_warn("A closing parenthesis ')' is missing in the dasd= parameter\n"); - rc = -EINVAL; - } else - str++; - *endp = str; - if (rc != 0) - return rc; - return features; + + return rc ? : features; } /* @@ -242,48 +221,38 @@ dasd_feature_list(char *str, char **endp) * action and return a pointer to the residual string. If the first element * could not be matched to any keyword then return an error code. */ -static char * -dasd_parse_keyword( char *parsestring ) { - - char *nextcomma, *residual_str; - int length; +static int __init dasd_parse_keyword(char *keyword) +{ + int length = strlen(keyword); - nextcomma = strchr(parsestring,','); - if (nextcomma) { - length = nextcomma - parsestring; - residual_str = nextcomma + 1; - } else { - length = strlen(parsestring); - residual_str = parsestring + length; - } - if (strncmp("autodetect", parsestring, length) == 0) { + if (strncmp("autodetect", keyword, length) == 0) { dasd_autodetect = 1; pr_info("The autodetection mode has been activated\n"); - return residual_str; + return 0; } - if (strncmp("probeonly", parsestring, length) == 0) { + if (strncmp("probeonly", keyword, length) == 0) { dasd_probeonly = 1; pr_info("The probeonly mode has been activated\n"); - return residual_str; + return 0; } - if (strncmp("nopav", parsestring, length) == 0) { + if (strncmp("nopav", keyword, length) == 0) { if (MACHINE_IS_VM) pr_info("'nopav' is not supported on z/VM\n"); else { dasd_nopav = 1; pr_info("PAV support has be deactivated\n"); } - return residual_str; + return 0; } - if (strncmp("nofcx", parsestring, length) == 0) { + if (strncmp("nofcx", keyword, length) == 0) { dasd_nofcx = 1; pr_info("High Performance FICON support has been " "deactivated\n"); - return residual_str; + return 0; } - if (strncmp("fixedbuffers", parsestring, length) == 0) { + if (strncmp("fixedbuffers", keyword, length) == 0) { if (dasd_page_cache) - return residual_str; + return 0; dasd_page_cache = kmem_cache_create("dasd_page_cache", PAGE_SIZE, PAGE_SIZE, SLAB_CACHE_DMA, @@ -294,73 +263,97 @@ dasd_parse_keyword( char *parsestring ) { else DBF_EVENT(DBF_INFO, "%s", "turning on fixed buffer mode"); - return residual_str; - } - return ERR_PTR(-EINVAL); + return 0; + } + + return -EINVAL; } /* - * Try to interprete the first element on the comma separated parse string - * as a device number or a range of devices. If the interpretation is - * successful, create the matching dasd_devmap entries and return a pointer - * to the residual string. - * If interpretation fails or in case of an error, return an error code. + * Split a string of a device range into its pieces and return the from, to, and + * feature parts separately. + * e.g.: + * 0.0.1234-0.0.5678(ro:erplog) -> from: 0.0.1234 to: 0.0.5678 features: ro:erplog + * 0.0.8765(raw) -> from: 0.0.8765 to: null features: raw + * 0x4321 -> from: 0x4321 to: null features: null */ -static char * -dasd_parse_range( char *parsestring ) { +static int __init dasd_evaluate_range_param(char *range, char **from_str, + char **to_str, char **features_str) +{ + int rc = 0; + /* Do we have a range or a single device? */ + if (strchr(range, '-')) { + *from_str = strsep(&range, "-"); + *to_str = strsep(&range, "("); + *features_str = strsep(&range, ")"); + } else { + *from_str = strsep(&range, "("); + *features_str = strsep(&range, ")"); + } + + if (*features_str && !range) { + pr_warn("A closing parenthesis ')' is missing in the dasd= parameter\n"); + rc = -EINVAL; + } + + return rc; +} + +/* + * Try to interprete the range string as a device number or a range of devices. + * If the interpretation is successful, create the matching dasd_devmap entries. + * If interpretation fails or in case of an error, return an error code. + */ +static int __init dasd_parse_range(const char *range) +{ struct dasd_devmap *devmap; int from, from_id0, from_id1; int to, to_id0, to_id1; - int features, rc; - char bus_id[DASD_BUS_ID_SIZE+1], *str; - - str = parsestring; - rc = dasd_busid(&str, &from_id0, &from_id1, &from); - if (rc == 0) { - to = from; - to_id0 = from_id0; - to_id1 = from_id1; - if (*str == '-') { - str++; - rc = dasd_busid(&str, &to_id0, &to_id1, &to); + int features; + char bus_id[DASD_BUS_ID_SIZE + 1]; + char *features_str = NULL; + char *from_str = NULL; + char *to_str = NULL; + size_t len = strlen(range) + 1; + char tmp[len]; + + strlcpy(tmp, range, len); + + if (dasd_evaluate_range_param(tmp, &from_str, &to_str, &features_str)) + goto out_err; + + if (dasd_busid(from_str, &from_id0, &from_id1, &from)) + goto out_err; + + to = from; + to_id0 = from_id0; + to_id1 = from_id1; + if (to_str) { + if (dasd_busid(to_str, &to_id0, &to_id1, &to)) + goto out_err; + if (from_id0 != to_id0 || from_id1 != to_id1 || from > to) { + pr_err("%s is not a valid device range\n", range); + goto out_err; } } - if (rc == 0 && - (from_id0 != to_id0 || from_id1 != to_id1 || from > to)) - rc = -EINVAL; - if (rc) { - pr_err("%s is not a valid device range\n", parsestring); - return ERR_PTR(rc); - } - features = dasd_feature_list(str, &str); + + features = dasd_feature_list(features_str); if (features < 0) - return ERR_PTR(-EINVAL); + goto out_err; /* each device in dasd= parameter should be set initially online */ features |= DASD_FEATURE_INITIAL_ONLINE; while (from <= to) { - sprintf(bus_id, "%01x.%01x.%04x", - from_id0, from_id1, from++); + sprintf(bus_id, "%01x.%01x.%04x", from_id0, from_id1, from++); devmap = dasd_add_busid(bus_id, features); if (IS_ERR(devmap)) - return (char *)devmap; + return PTR_ERR(devmap); } - if (*str == ',') - return str + 1; - if (*str == '\0') - return str; - pr_warn("The dasd= parameter value %s has an invalid ending\n", str); - return ERR_PTR(-EINVAL); -} -static char * -dasd_parse_next_element( char *parsestring ) { - char * residual_str; - residual_str = dasd_parse_keyword(parsestring); - if (!IS_ERR(residual_str)) - return residual_str; - residual_str = dasd_parse_range(parsestring); - return residual_str; + return 0; + +out_err: + return -EINVAL; } /* @@ -369,30 +362,27 @@ dasd_parse_next_element( char *parsestring ) { * keywords and device ranges. The parameters in that list will be stored as * separate elementes in dasd[]. */ -int -dasd_parse(void) +int __init dasd_parse(void) { int rc, i; - char *parsestring; + char *cur; rc = 0; for (i = 0; i < DASD_MAX_PARAMS; i++) { - if (dasd[i] == NULL) + cur = dasd[i]; + if (!cur) break; - parsestring = dasd[i]; - /* loop over the comma separated list in the parsestring */ - while (*parsestring) { - parsestring = dasd_parse_next_element(parsestring); - if(IS_ERR(parsestring)) { - rc = PTR_ERR(parsestring); - break; - } - } - if (rc) { - DBF_EVENT(DBF_ALERT, "%s", "invalid range found"); + if (*cur == '\0') + continue; + + rc = dasd_parse_keyword(cur); + if (rc) + rc = dasd_parse_range(cur); + + if (rc) break; - } } + return rc; } diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 24be210c10e5..518dba2732d5 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -805,7 +805,7 @@ struct dasd_device *dasd_device_from_devindex(int); void dasd_add_link_to_gendisk(struct gendisk *, struct dasd_device *); struct dasd_device *dasd_device_from_gendisk(struct gendisk *); -int dasd_parse(void); +int dasd_parse(void) __init; int dasd_busid_known(const char *); /* externals in dasd_gendisk.c */ -- cgit v1.2.3 From defc2a9b9899511445cfbaa2c3a6509964b71207 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Wed, 25 Jan 2017 14:08:20 +0100 Subject: s390/dasd: allow 0 for path_threshold attribute Allow 0 as valid input for the path_threshold attribute to deactivate the IFCC/CCC error handling. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_devmap.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 041bdc4429c1..1164b51d09f3 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -1518,14 +1518,12 @@ dasd_path_threshold_store(struct device *dev, struct device_attribute *attr, if (IS_ERR(device)) return -ENODEV; - if ((kstrtoul(buf, 10, &val) != 0) || - (val > DASD_THRHLD_MAX) || val == 0) { + if (kstrtoul(buf, 10, &val) != 0 || val > DASD_THRHLD_MAX) { dasd_put_device(device); return -EINVAL; } spin_lock_irqsave(get_ccwdev_lock(to_ccwdev(dev)), flags); - if (val) - device->path_thrhld = val; + device->path_thrhld = val; spin_unlock_irqrestore(get_ccwdev_lock(to_ccwdev(dev)), flags); dasd_put_device(device); return count; -- cgit v1.2.3 From 2202134e48a3b50320aeb9e3dd1186833e9d7e66 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Wed, 25 Jan 2017 14:47:32 +0100 Subject: s390/dasd: check for device error pointer within state change interrupts Check if the device pointer is valid. Just a sanity check since we already are in the int handler of the device. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 0e3fdfdbd098..f3099c8cc143 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1712,8 +1712,11 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, /* check for for attention message */ if (scsw_dstat(&irb->scsw) & DEV_STAT_ATTENTION) { device = dasd_device_from_cdev_locked(cdev); - device->discipline->check_attention(device, irb->esw.esw1.lpum); - dasd_put_device(device); + if (!IS_ERR(device)) { + device->discipline->check_attention(device, + irb->esw.esw1.lpum); + dasd_put_device(device); + } } if (!cqr) -- cgit v1.2.3 From ca732e111ff7017e79a0cbb8aa0636c6ce48eb7d Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Wed, 25 Jan 2017 16:56:41 +0100 Subject: s390/dasd: check blockdevice pointer before trying to sync blockdevice If safe offline is called for a DASD alias device a null pointer is passed to fsync_bdev. So check for existence of the blockdevice before calling fsync_bdev. Should not be a real world problem since safe offline for an alias device does not make sense and fsync_bdev can deal with a NULL pointer which it gets after successful NULL pointer dereferencing on s390. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index f3099c8cc143..6fb3fd5efc11 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -3601,10 +3601,11 @@ int dasd_generic_set_offline(struct ccw_device *cdev) * empty */ /* sync blockdev and partitions */ - rc = fsync_bdev(device->block->bdev); - if (rc != 0) - goto interrupted; - + if (device->block) { + rc = fsync_bdev(device->block->bdev); + if (rc != 0) + goto interrupted; + } /* schedule device tasklet and wait for completion */ dasd_schedule_device_bh(device); rc = wait_event_interruptible(shutdown_waitq, -- cgit v1.2.3 From ba21d0ea218983f269f957de267ca62fdbf96be3 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Wed, 25 Jan 2017 16:59:46 +0100 Subject: s390/dasd: correct inconsistent indenting Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eckd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index ade04216c970..0f1713727d4c 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -2543,8 +2543,8 @@ dasd_eckd_build_format(struct dasd_device *base, DASD_ECKD_CCW_WRITE_CKD_MT; ccw->flags = CCW_FLAG_SLI; ccw->count = 8; - ccw->cda = (__u32)(addr_t) ect; - ccw++; + ccw->cda = (__u32)(addr_t) ect; + ccw++; } } } -- cgit v1.2.3 From 1dd128a1ff1eb75ab45c62d9438acdcf5558d3b7 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 31 Jan 2017 16:57:22 +0100 Subject: scm_blk: remove unneeded REQ_TYPE_FS check Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- drivers/s390/block/scm_blk.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/scm_blk.c b/drivers/s390/block/scm_blk.c index 9f16ea6964ec..152de6817875 100644 --- a/drivers/s390/block/scm_blk.c +++ b/drivers/s390/block/scm_blk.c @@ -300,13 +300,6 @@ static void scm_blk_request(struct request_queue *rq) struct request *req; while ((req = blk_peek_request(rq))) { - if (req->cmd_type != REQ_TYPE_FS) { - blk_start_request(req); - blk_dump_rq_flags(req, KMSG_COMPONENT " bad request"); - __blk_end_request_all(req, -EIO); - continue; - } - if (!scm_permit_request(bdev, req)) goto out; -- cgit v1.2.3 From a63f53e34db8b49675448d03ae324f6c5bc04fe6 Mon Sep 17 00:00:00 2001 From: Gerald Schaefer Date: Mon, 30 Jan 2017 15:52:14 +0100 Subject: s390/dcssblk: fix device size calculation in dcssblk_direct_access() Since commit dd22f551 "block: Change direct_access calling convention", the device size calculation in dcssblk_direct_access() is off-by-one. This results in bdev_direct_access() always returning -ENXIO because the returned value is not page aligned. Fix this by adding 1 to the dev_sz calculation. Fixes: dd22f551 ("block: Change direct_access calling convention") Cc: # 4.0+ Signed-off-by: Gerald Schaefer Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dcssblk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/s390') diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 9d66b4fb174b..415d10a67b7a 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -892,7 +892,7 @@ dcssblk_direct_access (struct block_device *bdev, sector_t secnum, dev_info = bdev->bd_disk->private_data; if (!dev_info) return -ENODEV; - dev_sz = dev_info->end - dev_info->start; + dev_sz = dev_info->end - dev_info->start + 1; offset = secnum * 512; *kaddr = (void *) dev_info->start + offset; *pfn = __pfn_to_pfn_t(PFN_DOWN(dev_info->start + offset), PFN_DEV); -- cgit v1.2.3 From 1e4a382fdc0ba8d1a85b758c0811de3a3631085e Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Mon, 21 Nov 2016 13:37:48 +0100 Subject: s390/qdio: clear DSCI prior to scanning multiple input queues For devices with multiple input queues, tiqdio_call_inq_handlers() iterates over all input queues and clears the device's DSCI during each iteration. If the DSCI is re-armed during one of the later iterations, we therefore do not scan the previous queues again. The re-arming also raises a new adapter interrupt. But its handler does not trigger a rescan for the device, as the DSCI has already been erroneously cleared. This can result in queue stalls on devices with multiple input queues. Fix it by clearing the DSCI just once, prior to scanning the queues. As the code is moved in front of the loop, we also need to access the DSCI directly (ie irq->dsci) instead of going via each queue's parent pointer to the same irq. This is not a functional change, and a follow-up patch will clean up the other users. In practice, this bug only affects CQ-enabled HiperSockets devices, ie. devices with sysfs-attribute "hsuid" set. Setting a hsuid is needed for AF_IUCV socket applications that use HiperSockets communication. Fixes: 104ea556ee7f ("qdio: support asynchronous delivery of storage blocks") Cc: # v3.2+ Reviewed-by: Ursula Braun Signed-off-by: Julian Wiedmann Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio_thinint.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 5d06253c2a7a..30e9fbbff051 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -147,11 +147,11 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq) struct qdio_q *q; int i; - for_each_input_queue(irq, q, i) { - if (!references_shared_dsci(irq) && - has_multiple_inq_on_dsci(irq)) - xchg(q->irq_ptr->dsci, 0); + if (!references_shared_dsci(irq) && + has_multiple_inq_on_dsci(irq)) + xchg(irq->dsci, 0); + for_each_input_queue(irq, q, i) { if (q->u.in.queue_start_poll) { /* skip if polling is enabled or already in work */ if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED, -- cgit v1.2.3 From 67bb323bfee7db1af047ae15a5d7f8f5a52a09a6 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Mon, 21 Nov 2016 13:34:23 +0100 Subject: s390/qdio: clean up q->irq_ptr usage In tiqdio_call_inq_handlers(), we're looping over all input queues on the *same* irq. So instead of using the queues' back pointer, we can just access the irq directly. No functional change. Signed-off-by: Julian Wiedmann Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio_thinint.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 30e9fbbff051..90447e9611b4 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -161,11 +161,11 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq) } /* avoid dsci clear here, done after processing */ - q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr, - q->irq_ptr->int_parm); + q->u.in.queue_start_poll(irq->cdev, q->nr, + irq->int_parm); } else { - if (!shared_ind(q->irq_ptr)) - xchg(q->irq_ptr->dsci, 0); + if (!shared_ind(irq)) + xchg(irq->dsci, 0); /* * Call inbound processing but not directly -- cgit v1.2.3 From 72a01d0b6afb5862998d84c19ddc9e1c39a9588c Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Mon, 21 Nov 2016 11:19:57 +0100 Subject: s390/qdio: fix up tiqdio_thinint_handler() kerneldoc Missed in commit f4eae94f7137 ("s390/airq: simplify adapter interrupt code") Signed-off-by: Julian Wiedmann Reviewed-by: Steffen Maier Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio_thinint.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 90447e9611b4..8ad98a902a91 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -178,8 +178,7 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq) /** * tiqdio_thinint_handler - thin interrupt handler for qdio - * @alsi: pointer to adapter local summary indicator - * @data: NULL + * @airq: pointer to adapter interrupt descriptor */ static void tiqdio_thinint_handler(struct airq_struct *airq) { -- cgit v1.2.3 From f83435c42ee6e4233cc07130e2cf5022b95e1d32 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Mon, 21 Nov 2016 11:34:25 +0100 Subject: s390/qdio: improve some debug prints With multiple input queues, these DBFs turned out to be not very helpful... Signed-off-by: Julian Wiedmann Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio_main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 71bf9bded485..a4ad39ba3873 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -457,7 +457,7 @@ static inline void inbound_primed(struct qdio_q *q, int count) { int new; - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %02x", count); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim:%1d %02x", q->nr, count); /* for QEBSM the ACK was already set by EQBS */ if (is_qebsm(q)) { @@ -544,7 +544,8 @@ static int get_inbound_buffer_frontier(struct qdio_q *q) case SLSB_P_INPUT_ACK: if (q->irq_ptr->perf_stat_enabled) q->q_stats.nr_sbal_nop++; - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop"); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop:%1d %#02x", + q->nr, q->first_to_check); break; default: WARN_ON_ONCE(1); -- cgit v1.2.3 From b6a05c823fc573a65efc4466f174abf05f922e0f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 30 Jan 2017 13:18:58 +0100 Subject: scsi: remove eh_timed_out methods in the transport template Instead define the timeout behavior purely based on the host_template eh_timed_out method and wire up the existing transport implementations in the host templates. This also clears up the confusion that the transport template method overrides the host template one, so some drivers have to re-override the transport template one. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Reviewed-by: Tyrel Datwyler Signed-off-by: Martin K. Petersen --- drivers/ata/libata-eh.c | 1 + drivers/ata/libata-transport.c | 1 - drivers/ata/libata.h | 1 - drivers/infiniband/ulp/iser/iscsi_iser.c | 1 + drivers/infiniband/ulp/srp/ib_srp.c | 1 + drivers/message/fusion/mptfc.c | 1 + drivers/message/fusion/mptsas.c | 2 +- drivers/s390/scsi/zfcp_scsi.c | 1 + drivers/scsi/be2iscsi/be_main.c | 1 + drivers/scsi/bfa/bfad_im.c | 2 ++ drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 1 + drivers/scsi/bnx2i/bnx2i_iscsi.c | 1 + drivers/scsi/csiostor/csio_scsi.c | 2 ++ drivers/scsi/cxgbi/cxgb3i/cxgb3i.c | 1 + drivers/scsi/cxgbi/cxgb4i/cxgb4i.c | 1 + drivers/scsi/fcoe/fcoe.c | 1 + drivers/scsi/fnic/fnic_main.c | 1 + drivers/scsi/ibmvscsi/ibmvfc.c | 1 + drivers/scsi/ibmvscsi/ibmvscsi.c | 1 + drivers/scsi/iscsi_tcp.c | 1 + drivers/scsi/libiscsi.c | 5 ++--- drivers/scsi/lpfc/lpfc_scsi.c | 2 ++ drivers/scsi/qedi/qedi_iscsi.c | 1 + drivers/scsi/qla2xxx/qla_os.c | 1 + drivers/scsi/scsi_error.c | 4 +--- drivers/scsi/scsi_transport_fc.c | 9 ++++----- drivers/scsi/scsi_transport_srp.c | 5 ++--- drivers/scsi/storvsc_drv.c | 5 ----- include/linux/libata.h | 2 ++ include/scsi/libiscsi.h | 1 + include/scsi/scsi_transport.h | 11 ----------- include/scsi/scsi_transport_fc.h | 1 + include/scsi/scsi_transport_srp.h | 1 + 33 files changed, 38 insertions(+), 33 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 0e1ec37070d1..50ee10db160f 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -549,6 +549,7 @@ enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd) DPRINTK("EXIT, ret=%d\n", ret); return ret; } +EXPORT_SYMBOL(ata_scsi_timed_out); static void ata_eh_unload(struct ata_port *ap) { diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index 7ef16c085058..46698232e6bf 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -716,7 +716,6 @@ struct scsi_transport_template *ata_attach_transport(void) return NULL; i->t.eh_strategy_handler = ata_scsi_error; - i->t.eh_timed_out = ata_scsi_timed_out; i->t.user_scan = ata_scsi_user_scan; i->t.host_attrs.ac.attrs = &i->port_attrs[0]; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 8f3a5596dd67..06d479d1f302 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -159,7 +159,6 @@ extern unsigned long ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd); extern void ata_internal_cmd_timed_out(struct ata_device *dev, u8 cmd); extern void ata_eh_acquire(struct ata_port *ap); extern void ata_eh_release(struct ata_port *ap); -extern enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); extern void ata_scsi_error(struct Scsi_Host *host); extern void ata_eh_fastdrain_timerfn(unsigned long arg); extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc); diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c index 9104e6b8cac9..86fed1956d7d 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.c +++ b/drivers/infiniband/ulp/iser/iscsi_iser.c @@ -997,6 +997,7 @@ static struct scsi_host_template iscsi_iser_sht = { .change_queue_depth = scsi_change_queue_depth, .sg_tablesize = ISCSI_ISER_DEF_SG_TABLESIZE, .cmd_per_lun = ISER_DEF_CMD_PER_LUN, + .eh_timed_out = iscsi_eh_cmd_timed_out, .eh_abort_handler = iscsi_eh_abort, .eh_device_reset_handler= iscsi_eh_device_reset, .eh_target_reset_handler = iscsi_eh_recover_target, diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 8ddc07123193..7866cb0a8f00 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -2864,6 +2864,7 @@ static struct scsi_host_template srp_template = { .info = srp_target_info, .queuecommand = srp_queuecommand, .change_queue_depth = srp_change_queue_depth, + .eh_timed_out = srp_timed_out, .eh_abort_handler = srp_abort, .eh_device_reset_handler = srp_reset_device, .eh_host_reset_handler = srp_reset_host, diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c index add6a3a6ef0d..98eafae78576 100644 --- a/drivers/message/fusion/mptfc.c +++ b/drivers/message/fusion/mptfc.c @@ -119,6 +119,7 @@ static struct scsi_host_template mptfc_driver_template = { .target_destroy = mptfc_target_destroy, .slave_destroy = mptscsih_slave_destroy, .change_queue_depth = mptscsih_change_queue_depth, + .eh_timed_out = fc_eh_timed_out, .eh_abort_handler = mptfc_abort, .eh_device_reset_handler = mptfc_dev_reset, .eh_bus_reset_handler = mptfc_bus_reset, diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index 7ee1667acde4..4ce333643943 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c @@ -1983,6 +1983,7 @@ static struct scsi_host_template mptsas_driver_template = { .target_destroy = mptsas_target_destroy, .slave_destroy = mptscsih_slave_destroy, .change_queue_depth = mptscsih_change_queue_depth, + .eh_timed_out = mptsas_eh_timed_out, .eh_abort_handler = mptscsih_abort, .eh_device_reset_handler = mptscsih_dev_reset, .eh_host_reset_handler = mptscsih_host_reset, @@ -5398,7 +5399,6 @@ mptsas_init(void) sas_attach_transport(&mptsas_transport_functions); if (!mptsas_transport_template) return -ENODEV; - mptsas_transport_template->eh_timed_out = mptsas_eh_timed_out; mptsasDoneCtx = mpt_register(mptscsih_io_done, MPTSAS_DRIVER, "mptscsih_io_done"); diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 07ffdbb5107f..0678cf714c0e 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -330,6 +330,7 @@ static struct scsi_host_template zfcp_scsi_host_template = { .module = THIS_MODULE, .name = "zfcp", .queuecommand = zfcp_scsi_queuecommand, + .eh_timed_out = fc_eh_timed_out, .eh_abort_handler = zfcp_scsi_eh_abort_handler, .eh_device_reset_handler = zfcp_scsi_eh_device_reset_handler, .eh_target_reset_handler = zfcp_scsi_eh_target_reset_handler, diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index c9b9daa91091..32b2713cec93 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -389,6 +389,7 @@ static struct scsi_host_template beiscsi_sht = { .change_queue_depth = scsi_change_queue_depth, .slave_configure = beiscsi_slave_configure, .target_alloc = iscsi_target_alloc, + .eh_timed_out = iscsi_eh_cmd_timed_out, .eh_abort_handler = beiscsi_eh_abort, .eh_device_reset_handler = beiscsi_eh_device_reset, .eh_target_reset_handler = iscsi_eh_session_reset, diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c index 02d806012fa1..7eb0eef18fdd 100644 --- a/drivers/scsi/bfa/bfad_im.c +++ b/drivers/scsi/bfa/bfad_im.c @@ -813,6 +813,7 @@ struct scsi_host_template bfad_im_scsi_host_template = { .name = BFAD_DRIVER_NAME, .info = bfad_im_info, .queuecommand = bfad_im_queuecommand, + .eh_timed_out = fc_eh_timed_out, .eh_abort_handler = bfad_im_abort_handler, .eh_device_reset_handler = bfad_im_reset_lun_handler, .eh_bus_reset_handler = bfad_im_reset_bus_handler, @@ -835,6 +836,7 @@ struct scsi_host_template bfad_im_vport_template = { .name = BFAD_DRIVER_NAME, .info = bfad_im_info, .queuecommand = bfad_im_queuecommand, + .eh_timed_out = fc_eh_timed_out, .eh_abort_handler = bfad_im_abort_handler, .eh_device_reset_handler = bfad_im_reset_lun_handler, .eh_bus_reset_handler = bfad_im_reset_bus_handler, diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index c639d5a02656..b1e39f985ec9 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -2947,6 +2947,7 @@ static struct scsi_host_template bnx2fc_shost_template = { .module = THIS_MODULE, .name = "QLogic Offload FCoE Initiator", .queuecommand = bnx2fc_queuecommand, + .eh_timed_out = fc_eh_timed_out, .eh_abort_handler = bnx2fc_eh_abort, /* abts */ .eh_device_reset_handler = bnx2fc_eh_device_reset, /* lun reset */ .eh_target_reset_handler = bnx2fc_eh_target_reset, /* tgt reset */ diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index 133901fd3e35..f32a66f89d25 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -2259,6 +2259,7 @@ static struct scsi_host_template bnx2i_host_template = { .name = "QLogic Offload iSCSI Initiator", .proc_name = "bnx2i", .queuecommand = iscsi_queuecommand, + .eh_timed_out = iscsi_eh_cmd_timed_out, .eh_abort_handler = iscsi_eh_abort, .eh_device_reset_handler = iscsi_eh_device_reset, .eh_target_reset_handler = iscsi_eh_recover_target, diff --git a/drivers/scsi/csiostor/csio_scsi.c b/drivers/scsi/csiostor/csio_scsi.c index 89a52b941ea8..a1ff75f1384f 100644 --- a/drivers/scsi/csiostor/csio_scsi.c +++ b/drivers/scsi/csiostor/csio_scsi.c @@ -2270,6 +2270,7 @@ struct scsi_host_template csio_fcoe_shost_template = { .name = CSIO_DRV_DESC, .proc_name = KBUILD_MODNAME, .queuecommand = csio_queuecommand, + .eh_timed_out = fc_eh_timed_out, .eh_abort_handler = csio_eh_abort_handler, .eh_device_reset_handler = csio_eh_lun_reset_handler, .slave_alloc = csio_slave_alloc, @@ -2289,6 +2290,7 @@ struct scsi_host_template csio_fcoe_shost_vport_template = { .name = CSIO_DRV_DESC, .proc_name = KBUILD_MODNAME, .queuecommand = csio_queuecommand, + .eh_timed_out = fc_eh_timed_out, .eh_abort_handler = csio_eh_abort_handler, .eh_device_reset_handler = csio_eh_lun_reset_handler, .slave_alloc = csio_slave_alloc, diff --git a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c index 33e83464e091..1880eb6c68f7 100644 --- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c +++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c @@ -90,6 +90,7 @@ static struct scsi_host_template cxgb3i_host_template = { .sg_tablesize = SG_ALL, .max_sectors = 0xFFFF, .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN, + .eh_timed_out = iscsi_eh_cmd_timed_out, .eh_abort_handler = iscsi_eh_abort, .eh_device_reset_handler = iscsi_eh_device_reset, .eh_target_reset_handler = iscsi_eh_recover_target, diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index 9a2fdc305cf2..3fb3f5708ff7 100644 --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -103,6 +103,7 @@ static struct scsi_host_template cxgb4i_host_template = { .sg_tablesize = SG_ALL, .max_sectors = 0xFFFF, .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN, + .eh_timed_out = iscsi_eh_cmd_timed_out, .eh_abort_handler = iscsi_eh_abort, .eh_device_reset_handler = iscsi_eh_device_reset, .eh_target_reset_handler = iscsi_eh_recover_target, diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 59150cad0353..86af57f7c11a 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -277,6 +277,7 @@ static struct scsi_host_template fcoe_shost_template = { .name = "FCoE Driver", .proc_name = FCOE_NAME, .queuecommand = fc_queuecommand, + .eh_timed_out = fc_eh_timed_out, .eh_abort_handler = fc_eh_abort, .eh_device_reset_handler = fc_eh_device_reset, .eh_host_reset_handler = fc_eh_host_reset, diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 58ce9020d69c..ba58b7953263 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -106,6 +106,7 @@ static struct scsi_host_template fnic_host_template = { .module = THIS_MODULE, .name = DRV_NAME, .queuecommand = fnic_queuecommand, + .eh_timed_out = fc_eh_timed_out, .eh_abort_handler = fnic_abort_cmd, .eh_device_reset_handler = fnic_device_reset, .eh_host_reset_handler = fnic_host_reset, diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index 78b72c28a55d..2c92dabb55f6 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -3090,6 +3090,7 @@ static struct scsi_host_template driver_template = { .name = "IBM POWER Virtual FC Adapter", .proc_name = IBMVFC_NAME, .queuecommand = ibmvfc_queuecommand, + .eh_timed_out = fc_eh_timed_out, .eh_abort_handler = ibmvfc_eh_abort_handler, .eh_device_reset_handler = ibmvfc_eh_device_reset_handler, .eh_target_reset_handler = ibmvfc_eh_target_reset_handler, diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index 50cd01165e35..1deb0a9f14a6 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -2072,6 +2072,7 @@ static struct scsi_host_template driver_template = { .name = "IBM POWER Virtual SCSI Adapter " IBMVSCSI_VERSION, .proc_name = "ibmvscsi", .queuecommand = ibmvscsi_queuecommand, + .eh_timed_out = srp_timed_out, .eh_abort_handler = ibmvscsi_eh_abort_handler, .eh_device_reset_handler = ibmvscsi_eh_device_reset_handler, .eh_host_reset_handler = ibmvscsi_eh_host_reset_handler, diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index ace4f1f41b8e..4228aba1f654 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -967,6 +967,7 @@ static struct scsi_host_template iscsi_sw_tcp_sht = { .sg_tablesize = 4096, .max_sectors = 0xFFFF, .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN, + .eh_timed_out = iscsi_eh_cmd_timed_out, .eh_abort_handler = iscsi_eh_abort, .eh_device_reset_handler= iscsi_eh_device_reset, .eh_target_reset_handler = iscsi_eh_recover_target, diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index f9b6fba689ff..834d1212b6d5 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1930,7 +1930,7 @@ static int iscsi_has_ping_timed_out(struct iscsi_conn *conn) return 0; } -static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) +enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) { enum blk_eh_timer_return rc = BLK_EH_NOT_HANDLED; struct iscsi_task *task = NULL, *running_task; @@ -2063,6 +2063,7 @@ done: "timer reset" : "nh"); return rc; } +EXPORT_SYMBOL_GPL(iscsi_eh_cmd_timed_out); static void iscsi_check_transport_timeouts(unsigned long data) { @@ -2585,8 +2586,6 @@ int iscsi_host_add(struct Scsi_Host *shost, struct device *pdev) if (!shost->cmd_per_lun) shost->cmd_per_lun = ISCSI_DEF_CMD_PER_LUN; - if (!shost->transportt->eh_timed_out) - shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out; return scsi_add_host(shost, pdev); } EXPORT_SYMBOL_GPL(iscsi_host_add); diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 19d349fc889f..1180a22beb43 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -5929,6 +5929,7 @@ struct scsi_host_template lpfc_template = { .proc_name = LPFC_DRIVER_NAME, .info = lpfc_info, .queuecommand = lpfc_queuecommand, + .eh_timed_out = fc_eh_timed_out, .eh_abort_handler = lpfc_abort_handler, .eh_device_reset_handler = lpfc_device_reset_handler, .eh_target_reset_handler = lpfc_target_reset_handler, @@ -5955,6 +5956,7 @@ struct scsi_host_template lpfc_vport_template = { .proc_name = LPFC_DRIVER_NAME, .info = lpfc_info, .queuecommand = lpfc_queuecommand, + .eh_timed_out = fc_eh_timed_out, .eh_abort_handler = lpfc_abort_handler, .eh_device_reset_handler = lpfc_device_reset_handler, .eh_target_reset_handler = lpfc_target_reset_handler, diff --git a/drivers/scsi/qedi/qedi_iscsi.c b/drivers/scsi/qedi/qedi_iscsi.c index d6a205433b66..8b25f0a1eb8c 100644 --- a/drivers/scsi/qedi/qedi_iscsi.c +++ b/drivers/scsi/qedi/qedi_iscsi.c @@ -48,6 +48,7 @@ struct scsi_host_template qedi_host_template = { .name = "QLogic QEDI 25/40/100Gb iSCSI Initiator Driver", .proc_name = QEDI_MODULE_NAME, .queuecommand = iscsi_queuecommand, + .eh_timed_out = iscsi_eh_cmd_timed_out, .eh_abort_handler = iscsi_eh_abort, .eh_device_reset_handler = iscsi_eh_device_reset, .eh_target_reset_handler = iscsi_eh_recover_target, diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 8521cfe302e9..c5a9d6c295a0 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -262,6 +262,7 @@ struct scsi_host_template qla2xxx_driver_template = { .name = QLA2XXX_DRIVER_NAME, .queuecommand = qla2xxx_queuecommand, + .eh_timed_out = fc_eh_timed_out, .eh_abort_handler = qla2xxx_eh_abort, .eh_device_reset_handler = qla2xxx_eh_device_reset, .eh_target_reset_handler = qla2xxx_eh_target_reset, diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 996e134d79fa..9d7bfbb02389 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -279,9 +279,7 @@ enum blk_eh_timer_return scsi_times_out(struct request *req) if (host->eh_deadline != -1 && !host->last_reset) host->last_reset = jiffies; - if (host->transportt->eh_timed_out) - rtn = host->transportt->eh_timed_out(scmd); - else if (host->hostt->eh_timed_out) + if (host->hostt->eh_timed_out) rtn = host->hostt->eh_timed_out(scmd); if (rtn == BLK_EH_NOT_HANDLED) { diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 03577bde6ac5..9a6ea6fccb06 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -2055,7 +2055,7 @@ static int fc_vport_match(struct attribute_container *cont, /** - * fc_timed_out - FC Transport I/O timeout intercept handler + * fc_eh_timed_out - FC Transport I/O timeout intercept handler * @scmd: The SCSI command which timed out * * This routine protects against error handlers getting invoked while a @@ -2076,8 +2076,8 @@ static int fc_vport_match(struct attribute_container *cont, * Notes: * This routine assumes no locks are held on entry. */ -static enum blk_eh_timer_return -fc_timed_out(struct scsi_cmnd *scmd) +enum blk_eh_timer_return +fc_eh_timed_out(struct scsi_cmnd *scmd) { struct fc_rport *rport = starget_to_rport(scsi_target(scmd->device)); @@ -2086,6 +2086,7 @@ fc_timed_out(struct scsi_cmnd *scmd) return BLK_EH_NOT_HANDLED; } +EXPORT_SYMBOL(fc_eh_timed_out); /* * Called by fc_user_scan to locate an rport on the shost that @@ -2211,8 +2212,6 @@ fc_attach_transport(struct fc_function_template *ft) /* Transport uses the shost workq for scsi scanning */ i->t.create_work_queue = 1; - i->t.eh_timed_out = fc_timed_out; - i->t.user_scan = fc_user_scan; /* target-mode drivers' functions */ diff --git a/drivers/scsi/scsi_transport_srp.c b/drivers/scsi/scsi_transport_srp.c index b87a78673f65..75b57a1855b0 100644 --- a/drivers/scsi/scsi_transport_srp.c +++ b/drivers/scsi/scsi_transport_srp.c @@ -591,7 +591,7 @@ EXPORT_SYMBOL(srp_reconnect_rport); * Note: This function is called from soft-IRQ context and with the request * queue lock held. */ -static enum blk_eh_timer_return srp_timed_out(struct scsi_cmnd *scmd) +enum blk_eh_timer_return srp_timed_out(struct scsi_cmnd *scmd) { struct scsi_device *sdev = scmd->device; struct Scsi_Host *shost = sdev->host; @@ -603,6 +603,7 @@ static enum blk_eh_timer_return srp_timed_out(struct scsi_cmnd *scmd) i->f->reset_timer_if_blocked && scsi_device_blocked(sdev) ? BLK_EH_RESET_TIMER : BLK_EH_NOT_HANDLED; } +EXPORT_SYMBOL(srp_timed_out); static void srp_rport_release(struct device *dev) { @@ -820,8 +821,6 @@ srp_attach_transport(struct srp_function_template *ft) if (!i) return NULL; - i->t.eh_timed_out = srp_timed_out; - i->t.tsk_mgmt_response = srp_tsk_mgmt_response; i->t.it_nexus_response = srp_it_nexus_response; diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index ceadf6b4ebc1..585e54f6512c 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1899,11 +1899,6 @@ static int __init storvsc_drv_init(void) fc_transport_template = fc_attach_transport(&fc_transport_functions); if (!fc_transport_template) return -ENODEV; - - /* - * Install Hyper-V specific timeout handler. - */ - fc_transport_template->eh_timed_out = storvsc_eh_timed_out; #endif ret = vmbus_driver_register(&storvsc_drv); diff --git a/include/linux/libata.h b/include/linux/libata.h index c170be548b7f..46e18c0619c6 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1130,6 +1130,7 @@ extern int ata_sas_port_start(struct ata_port *ap); extern void ata_sas_port_stop(struct ata_port *ap); extern int ata_sas_slave_configure(struct scsi_device *, struct ata_port *); extern int ata_sas_queuecmd(struct scsi_cmnd *cmd, struct ata_port *ap); +extern enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); extern int sata_scr_valid(struct ata_link *link); extern int sata_scr_read(struct ata_link *link, int reg, u32 *val); extern int sata_scr_write(struct ata_link *link, int reg, u32 val); @@ -1355,6 +1356,7 @@ extern struct device_attribute *ata_common_sdev_attrs[]; .proc_name = drv_name, \ .slave_configure = ata_scsi_slave_config, \ .slave_destroy = ata_scsi_slave_destroy, \ + .eh_timed_out = ata_scsi_timed_out, \ .bios_param = ata_std_bios_param, \ .unlock_native_capacity = ata_scsi_unlock_native_capacity, \ .sdev_attrs = ata_common_sdev_attrs diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index 4d1c46aac331..b0e275de6dec 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -383,6 +383,7 @@ extern int iscsi_eh_recover_target(struct scsi_cmnd *sc); extern int iscsi_eh_session_reset(struct scsi_cmnd *sc); extern int iscsi_eh_device_reset(struct scsi_cmnd *sc); extern int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc); +extern enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc); /* * iSCSI host helpers. diff --git a/include/scsi/scsi_transport.h b/include/scsi/scsi_transport.h index 81292392adbc..6c3bb9f3dc0f 100644 --- a/include/scsi/scsi_transport.h +++ b/include/scsi/scsi_transport.h @@ -57,17 +57,6 @@ struct scsi_transport_template { */ void (* eh_strategy_handler)(struct Scsi_Host *); - /* - * This is an optional routine that allows the transport to become - * involved when a scsi io timer fires. The return value tells the - * timer routine how to finish the io timeout handling: - * EH_HANDLED: I fixed the error, please complete the command - * EH_RESET_TIMER: I need more time, reset the timer and - * begin counting again - * EH_NOT_HANDLED Begin normal error recovery - */ - enum blk_eh_timer_return (*eh_timed_out)(struct scsi_cmnd *); - /* * Used as callback for the completion of i_t_nexus request * for target drivers. diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index 924c8e614b45..b21b8aa58c4d 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h @@ -808,6 +808,7 @@ struct fc_vport *fc_vport_create(struct Scsi_Host *shost, int channel, struct fc_vport_identifiers *); int fc_vport_terminate(struct fc_vport *vport); int fc_block_scsi_eh(struct scsi_cmnd *cmnd); +enum blk_eh_timer_return fc_eh_timed_out(struct scsi_cmnd *scmd); static inline struct Scsi_Host *fc_bsg_to_shost(struct bsg_job *job) { diff --git a/include/scsi/scsi_transport_srp.h b/include/scsi/scsi_transport_srp.h index d40d3ef25707..20d96797edc5 100644 --- a/include/scsi/scsi_transport_srp.h +++ b/include/scsi/scsi_transport_srp.h @@ -124,6 +124,7 @@ extern int srp_reconnect_rport(struct srp_rport *rport); extern void srp_start_tl_fail_timers(struct srp_rport *rport); extern void srp_remove_host(struct Scsi_Host *); extern void srp_stop_rport_timers(struct srp_rport *rport); +enum blk_eh_timer_return srp_timed_out(struct scsi_cmnd *scmd); /** * srp_chkready() - evaluate the transport layer state before I/O -- cgit v1.2.3 From 9090f3feb3637dfdc20a5a4af88ed897b2fa894f Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 24 Jan 2017 15:45:13 +0100 Subject: s390/sclp: move early printk code to drivers Move the early sclp printk code to the drivers folder where also the rest of the sclp code can be found. This way it is possible to use the sclp private header files for further cleanups. Reviewed-by: Peter Oberparleiter Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/boot/compressed/Makefile | 3 +- arch/s390/kernel/Makefile | 10 +- arch/s390/kernel/sclp.c | 213 ------------------------------------ drivers/s390/char/Makefile | 16 ++- drivers/s390/char/sclp_early_core.c | 213 ++++++++++++++++++++++++++++++++++++ 5 files changed, 232 insertions(+), 223 deletions(-) delete mode 100644 arch/s390/kernel/sclp.c create mode 100644 drivers/s390/char/sclp_early_core.c (limited to 'drivers/s390') diff --git a/arch/s390/boot/compressed/Makefile b/arch/s390/boot/compressed/Makefile index 6bd2c9022be3..f7e4c834ea24 100644 --- a/arch/s390/boot/compressed/Makefile +++ b/arch/s390/boot/compressed/Makefile @@ -19,7 +19,8 @@ KBUILD_CFLAGS += $(call cc-option,-ffreestanding) GCOV_PROFILE := n UBSAN_SANITIZE := n -OBJECTS := $(addprefix $(objtree)/arch/s390/kernel/, head.o sclp.o ebcdic.o als.o) +OBJECTS := $(addprefix $(objtree)/arch/s390/kernel/, head.o ebcdic.o als.o) +OBJECTS += $(objtree)/drivers/s390/char/sclp_early_core.o OBJECTS += $(obj)/head.o $(obj)/misc.o $(obj)/piggy.o LDFLAGS_vmlinux := --oformat $(LD_BFD) -e startup -T diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index edbc62e04027..060ce548fe8b 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -10,31 +10,25 @@ CFLAGS_REMOVE_ftrace.o = $(CC_FLAGS_FTRACE) # Do not trace early setup code CFLAGS_REMOVE_als.o = $(CC_FLAGS_FTRACE) CFLAGS_REMOVE_early.o = $(CC_FLAGS_FTRACE) -CFLAGS_REMOVE_sclp.o = $(CC_FLAGS_FTRACE) endif GCOV_PROFILE_als.o := n GCOV_PROFILE_early.o := n -GCOV_PROFILE_sclp.o := n KCOV_INSTRUMENT_als.o := n KCOV_INSTRUMENT_early.o := n -KCOV_INSTRUMENT_sclp.o := n UBSAN_SANITIZE_als.o := n UBSAN_SANITIZE_early.o := n -UBSAN_SANITIZE_sclp.o := n # -# Use -march=z900 for sclp.c and als.c to be able to print an error +# Use -march=z900 for als.c to be able to print an error # message if the kernel is started on a machine which is too old # ifneq ($(CC_FLAGS_MARCH),-march=z900) CFLAGS_REMOVE_als.o += $(CC_FLAGS_MARCH) CFLAGS_als.o += -march=z900 -CFLAGS_REMOVE_sclp.o += $(CC_FLAGS_MARCH) -CFLAGS_sclp.o += -march=z900 AFLAGS_REMOVE_head.o += $(CC_FLAGS_MARCH) AFLAGS_head.o += -march=z900 endif @@ -61,7 +55,7 @@ CFLAGS_sysinfo.o += -w obj-y := traps.o time.o process.o base.o early.o setup.o idle.o vtime.o obj-y += processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o -obj-y += debug.o irq.o ipl.o dis.o diag.o sclp.o vdso.o als.o +obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o als.o obj-y += sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o obj-y += runtime_instr.o cache.o fpu.o dumpstack.o obj-y += entry.o reipl.o relocate_kernel.o diff --git a/arch/s390/kernel/sclp.c b/arch/s390/kernel/sclp.c deleted file mode 100644 index f9c5b02d2685..000000000000 --- a/arch/s390/kernel/sclp.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright IBM Corp. 2015 - * Author(s): Martin Schwidefsky - */ -#include -#include -#include -#include -#include -#include - -#define EVTYP_VT220MSG_MASK 0x00000040 -#define EVTYP_MSG_MASK 0x40000000 - -static char _sclp_work_area[4096] __aligned(PAGE_SIZE) __section(data); -static bool have_vt220 __section(data); -static bool have_linemode __section(data); - -static void _sclp_wait_int(void) -{ - unsigned long psw_mask, addr, flags; - psw_t psw_ext_save, psw_wait; - union ctlreg0 cr0, cr0_new; - - raw_local_irq_save(flags); - __ctl_store(cr0.val, 0, 0); - cr0_new.val = cr0.val & ~CR0_IRQ_SUBCLASS_MASK; - cr0_new.lap = 0; - cr0_new.sssm = 1; - __ctl_load(cr0_new.val, 0, 0); - - psw_ext_save = S390_lowcore.external_new_psw; - psw_mask = __extract_psw(); - S390_lowcore.external_new_psw.mask = psw_mask; - psw_wait.mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT; - S390_lowcore.ext_int_code = 0; - - do { - asm volatile( - " larl %[addr],0f\n" - " stg %[addr],%[psw_wait_addr]\n" - " stg %[addr],%[psw_ext_addr]\n" - " lpswe %[psw_wait]\n" - "0:\n" - : [addr] "=&d" (addr), - [psw_wait_addr] "=Q" (psw_wait.addr), - [psw_ext_addr] "=Q" (S390_lowcore.external_new_psw.addr) - : [psw_wait] "Q" (psw_wait) - : "cc", "memory"); - } while (S390_lowcore.ext_int_code != EXT_IRQ_SERVICE_SIG); - - S390_lowcore.external_new_psw = psw_ext_save; - __ctl_load(cr0.val, 0, 0); - raw_local_irq_restore(flags); -} - -static int _sclp_servc(unsigned int cmd, char *sccb) -{ - unsigned int cc; - - do { - asm volatile( - " .insn rre,0xb2200000,%1,%2\n" - " ipm %0\n" - : "=d" (cc) : "d" (cmd), "a" (sccb) - : "cc", "memory"); - cc >>= 28; - if (cc == 3) - return -EINVAL; - _sclp_wait_int(); - } while (cc != 0); - return (*(unsigned short *)(sccb + 6) == 0x20) ? 0 : -EIO; -} - -static int _sclp_setup(int disable) -{ - static unsigned char init_sccb[] = { - 0x00, 0x1c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x04, - 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - unsigned int *masks; - int rc; - - memcpy(_sclp_work_area, init_sccb, 28); - masks = (unsigned int *)(_sclp_work_area + 12); - if (disable) - memset(masks, 0, 16); - /* SCLP write mask */ - rc = _sclp_servc(0x00780005, _sclp_work_area); - if (rc) - return rc; - have_vt220 = masks[2] & EVTYP_VT220MSG_MASK; - have_linemode = masks[2] & EVTYP_MSG_MASK; - return 0; -} - -/* Output multi-line text using SCLP Message interface. */ -static void _sclp_print_lm(const char *str, unsigned int len) -{ - static unsigned char write_head[] = { - /* sccb header */ - 0x00, 0x52, /* 0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 2 */ - /* evbuf */ - 0x00, 0x4a, /* 8 */ - 0x02, 0x00, 0x00, 0x00, /* 10 */ - /* mdb */ - 0x00, 0x44, /* 14 */ - 0x00, 0x01, /* 16 */ - 0xd4, 0xc4, 0xc2, 0x40, /* 18 */ - 0x00, 0x00, 0x00, 0x01, /* 22 */ - /* go */ - 0x00, 0x38, /* 26 */ - 0x00, 0x01, /* 28 */ - 0x00, 0x00, 0x00, 0x00, /* 30 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 34 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 42 */ - 0x00, 0x00, 0x00, 0x00, /* 50 */ - 0x00, 0x00, /* 54 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 56 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 64 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 72 */ - 0x00, 0x00, /* 80 */ - }; - static unsigned char write_mto[] = { - /* mto */ - 0x00, 0x0a, /* 0 */ - 0x00, 0x04, /* 2 */ - 0x10, 0x00, /* 4 */ - 0x00, 0x00, 0x00, 0x00 /* 6 */ - }; - unsigned char *ptr, *end_ptr, ch; - unsigned int count, num; - - num = 0; - memcpy(_sclp_work_area, write_head, sizeof(write_head)); - ptr = _sclp_work_area + sizeof(write_head); - end_ptr = _sclp_work_area + sizeof(_sclp_work_area) - 1; - do { - if (ptr + sizeof(write_mto) > end_ptr) - break; - memcpy(ptr, write_mto, sizeof(write_mto)); - for (count = sizeof(write_mto); num < len; count++) { - num++; - ch = *str++; - if (ch == 0x0a) - break; - if (ptr > end_ptr) - break; - ptr[count] = _ascebc[ch]; - } - /* Update length fields in mto, mdb, evbuf and sccb */ - *(unsigned short *) ptr = count; - *(unsigned short *)(_sclp_work_area + 14) += count; - *(unsigned short *)(_sclp_work_area + 8) += count; - *(unsigned short *)(_sclp_work_area + 0) += count; - ptr += count; - } while (num < len); - - /* SCLP write data */ - _sclp_servc(0x00760005, _sclp_work_area); -} - -/* Output multi-line text (plus a newline) using SCLP VT220 - * interface. - */ -static void _sclp_print_vt220(const char *str, unsigned int len) -{ - static unsigned char const write_head[] = { - /* sccb header */ - 0x00, 0x0e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* evbuf header */ - 0x00, 0x06, - 0x1a, 0x00, 0x00, 0x00, - }; - - if (sizeof(write_head) + len >= sizeof(_sclp_work_area)) - len = sizeof(_sclp_work_area) - sizeof(write_head) - 1; - - memcpy(_sclp_work_area, write_head, sizeof(write_head)); - memcpy(_sclp_work_area + sizeof(write_head), str, len); - _sclp_work_area[sizeof(write_head) + len] = '\n'; - - /* Update length fields in evbuf and sccb headers */ - *(unsigned short *)(_sclp_work_area + 8) += len + 1; - *(unsigned short *)(_sclp_work_area + 0) += len + 1; - - /* SCLP write data */ - (void)_sclp_servc(0x00760005, _sclp_work_area); -} - -/* Output one or more lines of text on the SCLP console (VT220 and / - * or line-mode). All lines get terminated; no need for a trailing LF. - */ -void __sclp_print_early(const char *str, unsigned int len) -{ - if (_sclp_setup(0) != 0) - return; - if (have_linemode) - _sclp_print_lm(str, len); - if (have_vt220) - _sclp_print_vt220(str, len); - _sclp_setup(1); -} - -void _sclp_print_early(const char *str) -{ - __sclp_print_early(str, strlen(str)); -} diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 41e28b23b26a..0c443e26835d 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -2,9 +2,23 @@ # S/390 character devices # +ifdef CONFIG_FUNCTION_TRACER +# Do not trace early setup code +CFLAGS_REMOVE_sclp_early_core.o = $(CC_FLAGS_FTRACE) +endif + +GCOV_PROFILE_sclp_early_core.o := n +KCOV_INSTRUMENT_sclp_early_core.o := n +UBSAN_SANITIZE_sclp_early_core.o := n + +ifneq ($(CC_FLAGS_MARCH),-march=z900) +CFLAGS_REMOVE_sclp_early_core.o += $(CC_FLAGS_MARCH) +CFLAGS_sclp_early_core.o += -march=z900 +endif + obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o \ - sclp_early.o + sclp_early.o sclp_early_core.o obj-$(CONFIG_TN3270) += raw3270.o obj-$(CONFIG_TN3270_CONSOLE) += con3270.o diff --git a/drivers/s390/char/sclp_early_core.c b/drivers/s390/char/sclp_early_core.c new file mode 100644 index 000000000000..f9c5b02d2685 --- /dev/null +++ b/drivers/s390/char/sclp_early_core.c @@ -0,0 +1,213 @@ +/* + * Copyright IBM Corp. 2015 + * Author(s): Martin Schwidefsky + */ +#include +#include +#include +#include +#include +#include + +#define EVTYP_VT220MSG_MASK 0x00000040 +#define EVTYP_MSG_MASK 0x40000000 + +static char _sclp_work_area[4096] __aligned(PAGE_SIZE) __section(data); +static bool have_vt220 __section(data); +static bool have_linemode __section(data); + +static void _sclp_wait_int(void) +{ + unsigned long psw_mask, addr, flags; + psw_t psw_ext_save, psw_wait; + union ctlreg0 cr0, cr0_new; + + raw_local_irq_save(flags); + __ctl_store(cr0.val, 0, 0); + cr0_new.val = cr0.val & ~CR0_IRQ_SUBCLASS_MASK; + cr0_new.lap = 0; + cr0_new.sssm = 1; + __ctl_load(cr0_new.val, 0, 0); + + psw_ext_save = S390_lowcore.external_new_psw; + psw_mask = __extract_psw(); + S390_lowcore.external_new_psw.mask = psw_mask; + psw_wait.mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT; + S390_lowcore.ext_int_code = 0; + + do { + asm volatile( + " larl %[addr],0f\n" + " stg %[addr],%[psw_wait_addr]\n" + " stg %[addr],%[psw_ext_addr]\n" + " lpswe %[psw_wait]\n" + "0:\n" + : [addr] "=&d" (addr), + [psw_wait_addr] "=Q" (psw_wait.addr), + [psw_ext_addr] "=Q" (S390_lowcore.external_new_psw.addr) + : [psw_wait] "Q" (psw_wait) + : "cc", "memory"); + } while (S390_lowcore.ext_int_code != EXT_IRQ_SERVICE_SIG); + + S390_lowcore.external_new_psw = psw_ext_save; + __ctl_load(cr0.val, 0, 0); + raw_local_irq_restore(flags); +} + +static int _sclp_servc(unsigned int cmd, char *sccb) +{ + unsigned int cc; + + do { + asm volatile( + " .insn rre,0xb2200000,%1,%2\n" + " ipm %0\n" + : "=d" (cc) : "d" (cmd), "a" (sccb) + : "cc", "memory"); + cc >>= 28; + if (cc == 3) + return -EINVAL; + _sclp_wait_int(); + } while (cc != 0); + return (*(unsigned short *)(sccb + 6) == 0x20) ? 0 : -EIO; +} + +static int _sclp_setup(int disable) +{ + static unsigned char init_sccb[] = { + 0x00, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, + 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + unsigned int *masks; + int rc; + + memcpy(_sclp_work_area, init_sccb, 28); + masks = (unsigned int *)(_sclp_work_area + 12); + if (disable) + memset(masks, 0, 16); + /* SCLP write mask */ + rc = _sclp_servc(0x00780005, _sclp_work_area); + if (rc) + return rc; + have_vt220 = masks[2] & EVTYP_VT220MSG_MASK; + have_linemode = masks[2] & EVTYP_MSG_MASK; + return 0; +} + +/* Output multi-line text using SCLP Message interface. */ +static void _sclp_print_lm(const char *str, unsigned int len) +{ + static unsigned char write_head[] = { + /* sccb header */ + 0x00, 0x52, /* 0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 2 */ + /* evbuf */ + 0x00, 0x4a, /* 8 */ + 0x02, 0x00, 0x00, 0x00, /* 10 */ + /* mdb */ + 0x00, 0x44, /* 14 */ + 0x00, 0x01, /* 16 */ + 0xd4, 0xc4, 0xc2, 0x40, /* 18 */ + 0x00, 0x00, 0x00, 0x01, /* 22 */ + /* go */ + 0x00, 0x38, /* 26 */ + 0x00, 0x01, /* 28 */ + 0x00, 0x00, 0x00, 0x00, /* 30 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 34 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 42 */ + 0x00, 0x00, 0x00, 0x00, /* 50 */ + 0x00, 0x00, /* 54 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 56 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 64 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 72 */ + 0x00, 0x00, /* 80 */ + }; + static unsigned char write_mto[] = { + /* mto */ + 0x00, 0x0a, /* 0 */ + 0x00, 0x04, /* 2 */ + 0x10, 0x00, /* 4 */ + 0x00, 0x00, 0x00, 0x00 /* 6 */ + }; + unsigned char *ptr, *end_ptr, ch; + unsigned int count, num; + + num = 0; + memcpy(_sclp_work_area, write_head, sizeof(write_head)); + ptr = _sclp_work_area + sizeof(write_head); + end_ptr = _sclp_work_area + sizeof(_sclp_work_area) - 1; + do { + if (ptr + sizeof(write_mto) > end_ptr) + break; + memcpy(ptr, write_mto, sizeof(write_mto)); + for (count = sizeof(write_mto); num < len; count++) { + num++; + ch = *str++; + if (ch == 0x0a) + break; + if (ptr > end_ptr) + break; + ptr[count] = _ascebc[ch]; + } + /* Update length fields in mto, mdb, evbuf and sccb */ + *(unsigned short *) ptr = count; + *(unsigned short *)(_sclp_work_area + 14) += count; + *(unsigned short *)(_sclp_work_area + 8) += count; + *(unsigned short *)(_sclp_work_area + 0) += count; + ptr += count; + } while (num < len); + + /* SCLP write data */ + _sclp_servc(0x00760005, _sclp_work_area); +} + +/* Output multi-line text (plus a newline) using SCLP VT220 + * interface. + */ +static void _sclp_print_vt220(const char *str, unsigned int len) +{ + static unsigned char const write_head[] = { + /* sccb header */ + 0x00, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* evbuf header */ + 0x00, 0x06, + 0x1a, 0x00, 0x00, 0x00, + }; + + if (sizeof(write_head) + len >= sizeof(_sclp_work_area)) + len = sizeof(_sclp_work_area) - sizeof(write_head) - 1; + + memcpy(_sclp_work_area, write_head, sizeof(write_head)); + memcpy(_sclp_work_area + sizeof(write_head), str, len); + _sclp_work_area[sizeof(write_head) + len] = '\n'; + + /* Update length fields in evbuf and sccb headers */ + *(unsigned short *)(_sclp_work_area + 8) += len + 1; + *(unsigned short *)(_sclp_work_area + 0) += len + 1; + + /* SCLP write data */ + (void)_sclp_servc(0x00760005, _sclp_work_area); +} + +/* Output one or more lines of text on the SCLP console (VT220 and / + * or line-mode). All lines get terminated; no need for a trailing LF. + */ +void __sclp_print_early(const char *str, unsigned int len) +{ + if (_sclp_setup(0) != 0) + return; + if (have_linemode) + _sclp_print_lm(str, len); + if (have_vt220) + _sclp_print_vt220(str, len); + _sclp_setup(1); +} + +void _sclp_print_early(const char *str) +{ + __sclp_print_early(str, strlen(str)); +} -- cgit v1.2.3 From 76fdf1416eed264dee18aa7db3a32dcfa8572e03 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 27 Jan 2017 15:54:57 +0100 Subject: s390/sclp: disable early sclp code as soon as the base sclp driver is active Make sure the early sclp code does not generate any sclp requests anymore as soon as the base sclp driver is active. Otherwise both drivers may see unexpected requests or may miss expected interrupts. Reviewed-by: Peter Oberparleiter Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/char/sclp.c | 7 ------- drivers/s390/char/sclp.h | 7 +++++++ drivers/s390/char/sclp_early_core.c | 4 ++++ 3 files changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 272898225dbb..befc07acd3e0 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -94,13 +94,6 @@ static struct timer_list sclp_request_timer; /* Timer for queued requests. */ static struct timer_list sclp_queue_timer; -/* Internal state: is the driver initialized? */ -static volatile enum sclp_init_state_t { - sclp_init_state_uninitialized, - sclp_init_state_initializing, - sclp_init_state_initialized -} sclp_init_state = sclp_init_state_uninitialized; - /* Internal state: is a request active at the sclp? */ static volatile enum sclp_running_state_t { sclp_running_state_idle, diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index e1fc7eb043d6..0c1fa376df9e 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -211,6 +211,13 @@ int sclp_sync_request_timeout(sclp_cmdw_t command, void *sccb, int timeout); int sclp_sdias_init(void); void sclp_sdias_exit(void); +enum { + sclp_init_state_uninitialized, + sclp_init_state_initializing, + sclp_init_state_initialized +}; + +extern int sclp_init_state; extern int sclp_console_pages; extern int sclp_console_drop; extern unsigned long sclp_console_full; diff --git a/drivers/s390/char/sclp_early_core.c b/drivers/s390/char/sclp_early_core.c index f9c5b02d2685..2723ab56fb8f 100644 --- a/drivers/s390/char/sclp_early_core.c +++ b/drivers/s390/char/sclp_early_core.c @@ -16,6 +16,8 @@ static char _sclp_work_area[4096] __aligned(PAGE_SIZE) __section(data); static bool have_vt220 __section(data); static bool have_linemode __section(data); +int sclp_init_state __section(data) = sclp_init_state_uninitialized; + static void _sclp_wait_int(void) { unsigned long psw_mask, addr, flags; @@ -198,6 +200,8 @@ static void _sclp_print_vt220(const char *str, unsigned int len) */ void __sclp_print_early(const char *str, unsigned int len) { + if (sclp_init_state != sclp_init_state_uninitialized) + return; if (_sclp_setup(0) != 0) return; if (have_linemode) -- cgit v1.2.3 From d5ab7a34f9bbad54f89b812e6b0d2d898f9433db Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 24 Jan 2017 15:58:52 +0100 Subject: s390/sclp: make early sclp code readable This patch - unifies the old sclp early code and the sclp early printk code, so they can use common functions - makes sure all sclp early functions and variables have the same "sclp_early" prefix - converts the sclp early printk code into readable code by using existing data structures instead of hard coded magic arrays - splits the early sclp code into two files: sclp_early.c and sclp_early_core.c. The core file contains everything that is required by the kernel decompressor and may not call functions not contained within the core file. Otherwise the result would be a link error. - changes interrupt handling to be completely synchronous. The old early sclp code had a small window which allowed to receive several interrupts instead of exactly the single expected interrupt. This did hide a subtle potential bug, which is fixed with this large rework. - contains a couple of small cleanups. Reviewed-by: Peter Oberparleiter Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/boot/compressed/misc.c | 2 +- arch/s390/include/asm/sclp.h | 13 +- arch/s390/kernel/als.c | 10 +- arch/s390/kernel/early_printk.c | 2 +- arch/s390/kernel/ipl.c | 2 +- arch/s390/kernel/swsusp.S | 2 +- drivers/s390/char/sclp.c | 25 ---- drivers/s390/char/sclp.h | 34 ++++- drivers/s390/char/sclp_early.c | 178 ++++++++--------------- drivers/s390/char/sclp_early_core.c | 271 ++++++++++++++++++------------------ 10 files changed, 244 insertions(+), 295 deletions(-) (limited to 'drivers/s390') diff --git a/arch/s390/boot/compressed/misc.c b/arch/s390/boot/compressed/misc.c index 8515dd5a5663..fa95041fa9f6 100644 --- a/arch/s390/boot/compressed/misc.c +++ b/arch/s390/boot/compressed/misc.c @@ -66,7 +66,7 @@ static unsigned long free_mem_end_ptr; static int puts(const char *s) { - _sclp_print_early(s); + sclp_early_printk(s); return 0; } diff --git a/arch/s390/include/asm/sclp.h b/arch/s390/include/asm/sclp.h index 415eaace3f1b..ace3bd315438 100644 --- a/arch/s390/include/asm/sclp.h +++ b/arch/s390/include/asm/sclp.h @@ -101,7 +101,12 @@ struct zpci_report_error_header { u8 data[0]; /* Subsequent Data passed verbatim to SCLP ET 24 */ } __packed; -int _sclp_get_core_info_early(struct sclp_core_info *info); +int sclp_early_get_core_info(struct sclp_core_info *info); +void sclp_early_get_ipl_info(struct sclp_ipl_info *info); +void sclp_early_detect(void); +void sclp_early_printk(const char *s); +void __sclp_early_printk(const char *s, unsigned int len); + int _sclp_get_core_info(struct sclp_core_info *info); int sclp_core_configure(u8 core); int sclp_core_deconfigure(u8 core); @@ -110,21 +115,17 @@ int sclp_sdias_copy(void *dest, int blk_num, int nr_blks); int sclp_chp_configure(struct chp_id chpid); int sclp_chp_deconfigure(struct chp_id chpid); int sclp_chp_read_info(struct sclp_chp_info *info); -void sclp_get_ipl_info(struct sclp_ipl_info *info); int sclp_pci_configure(u32 fid); int sclp_pci_deconfigure(u32 fid); int sclp_pci_report(struct zpci_report_error_header *report, u32 fh, u32 fid); int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count); int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count); -void sclp_early_detect(void); -void _sclp_print_early(const char *); -void __sclp_print_early(const char *s, unsigned int len); void sclp_ocf_cpc_name_copy(char *dst); static inline int sclp_get_core_info(struct sclp_core_info *info, int early) { if (early) - return _sclp_get_core_info_early(info); + return sclp_early_get_core_info(info); return _sclp_get_core_info(info); } diff --git a/arch/s390/kernel/als.c b/arch/s390/kernel/als.c index a16e9d1bf9e3..0b3a06f05f90 100644 --- a/arch/s390/kernel/als.c +++ b/arch/s390/kernel/als.c @@ -41,7 +41,7 @@ static void __init print_machine_type(void) get_cpu_id(&id); u16_to_hex(type_str, id.machine); strcat(mach_str, type_str); - _sclp_print_early(mach_str); + sclp_early_printk(mach_str); } static void __init u16_to_decimal(char *str, u16 val) @@ -79,7 +79,7 @@ static void __init print_missing_facilities(void) * z/VM adds a four character prefix. */ if (strlen(als_str) > 70) { - _sclp_print_early(als_str); + sclp_early_printk(als_str); *als_str = '\0'; } u16_to_decimal(val_str, i * BITS_PER_LONG + j); @@ -87,13 +87,13 @@ static void __init print_missing_facilities(void) first = 0; } } - _sclp_print_early(als_str); - _sclp_print_early("See Principles of Operations for facility bits"); + sclp_early_printk(als_str); + sclp_early_printk("See Principles of Operations for facility bits"); } static void __init facility_mismatch(void) { - _sclp_print_early("The Linux kernel requires more recent processor hardware"); + sclp_early_printk("The Linux kernel requires more recent processor hardware"); print_machine_type(); print_missing_facilities(); disabled_wait(0x8badcccc); diff --git a/arch/s390/kernel/early_printk.c b/arch/s390/kernel/early_printk.c index 54a4dc582b81..819cb15c67e8 100644 --- a/arch/s390/kernel/early_printk.c +++ b/arch/s390/kernel/early_printk.c @@ -9,7 +9,7 @@ static void sclp_early_write(struct console *con, const char *s, unsigned int len) { - __sclp_print_early(s, len); + __sclp_early_printk(s, len); } static struct console sclp_early_console = { diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 2c6ddced5394..5b91308f6117 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -1864,7 +1864,7 @@ static int __init s390_ipl_init(void) { char str[8] = {0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40}; - sclp_get_ipl_info(&sclp_ipl_info); + sclp_early_get_ipl_info(&sclp_ipl_info); /* * Fix loadparm: There are systems where the (SCSI) LOADPARM * returned by read SCP info is invalid (contains EBCDIC blanks) diff --git a/arch/s390/kernel/swsusp.S b/arch/s390/kernel/swsusp.S index 1ff21f05d7dd..6c56fb89f553 100644 --- a/arch/s390/kernel/swsusp.S +++ b/arch/s390/kernel/swsusp.S @@ -196,7 +196,7 @@ pgm_check_entry: larl %r15,init_thread_union ahi %r15,1<<(PAGE_SHIFT+THREAD_SIZE_ORDER) larl %r2,.Lpanic_string - larl %r3,_sclp_print_early + larl %r3,sclp_early_printk lghi %r1,0 sam31 sigp %r1,%r0,SIGP_SET_ARCHITECTURE diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index befc07acd3e0..9c471ea1b99c 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -140,31 +140,6 @@ static void __sclp_make_read_req(void); static int sclp_init_mask(int calculate); static int sclp_init(void); -/* Perform service call. Return 0 on success, non-zero otherwise. */ -int -sclp_service_call(sclp_cmdw_t command, void *sccb) -{ - int cc = 4; /* Initialize for program check handling */ - - asm volatile( - "0: .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ - "1: ipm %0\n" - " srl %0,28\n" - "2:\n" - EX_TABLE(0b, 2b) - EX_TABLE(1b, 2b) - : "+&d" (cc) : "d" (command), "a" (__pa(sccb)) - : "cc", "memory"); - if (cc == 4) - return -EINVAL; - if (cc == 3) - return -EIO; - if (cc == 2) - return -EBUSY; - return 0; -} - - static void __sclp_queue_read_req(void) { diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index 0c1fa376df9e..78d5f542d979 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -204,7 +204,6 @@ void sclp_unregister(struct sclp_register *reg); int sclp_remove_processed(struct sccb_header *sccb); int sclp_deactivate(void); int sclp_reactivate(void); -int sclp_service_call(sclp_cmdw_t command, void *sccb); int sclp_sync_request(sclp_cmdw_t command, void *sccb); int sclp_sync_request_timeout(sclp_cmdw_t command, void *sccb, int timeout); @@ -222,8 +221,41 @@ extern int sclp_console_pages; extern int sclp_console_drop; extern unsigned long sclp_console_full; +extern char sclp_early_sccb[PAGE_SIZE]; + +void sclp_early_wait_irq(void); +int sclp_early_cmd_sync(sclp_cmdw_t cmd, void *sccb); +int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb); +unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb); +int sclp_early_set_event_mask(struct init_sccb *sccb, + unsigned long receive_mask, + unsigned long send_mask); + /* useful inlines */ +/* Perform service call. Return 0 on success, non-zero otherwise. */ +static inline int sclp_service_call(sclp_cmdw_t command, void *sccb) +{ + int cc = 4; /* Initialize for program check handling */ + + asm volatile( + "0: .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ + "1: ipm %0\n" + " srl %0,28\n" + "2:\n" + EX_TABLE(0b, 2b) + EX_TABLE(1b, 2b) + : "+&d" (cc) : "d" (command), "a" ((unsigned long)sccb) + : "cc", "memory"); + if (cc == 4) + return -EINVAL; + if (cc == 3) + return -EIO; + if (cc == 2) + return -EBUSY; + return 0; +} + /* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */ /* translate single character from ASCII to EBCDIC */ static inline unsigned char diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c index f8e46c22e641..2f9e50379e64 100644 --- a/drivers/s390/char/sclp_early.c +++ b/drivers/s390/char/sclp_early.c @@ -55,31 +55,12 @@ struct read_info_sccb { u8 _pad_128[4096 - 128]; /* 128-4095 */ } __packed __aligned(PAGE_SIZE); -static char sccb_early[PAGE_SIZE] __aligned(PAGE_SIZE) __initdata; static struct sclp_ipl_info sclp_ipl_info; struct sclp_info sclp; EXPORT_SYMBOL(sclp); -static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb) -{ - int rc; - - __ctl_set_bit(0, 9); - rc = sclp_service_call(cmd, sccb); - if (rc) - goto out; - __load_psw_mask(PSW_DEFAULT_KEY | PSW_MASK_BASE | PSW_MASK_EA | - PSW_MASK_BA | PSW_MASK_EXT | PSW_MASK_WAIT); - local_irq_disable(); -out: - /* Contents of the sccb might have changed. */ - barrier(); - __ctl_clear_bit(0, 9); - return rc; -} - -static int __init sclp_read_info_early(struct read_info_sccb *sccb) +static int __init sclp_early_read_info(struct read_info_sccb *sccb) { int rc, i; sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED, @@ -91,7 +72,7 @@ static int __init sclp_read_info_early(struct read_info_sccb *sccb) sccb->header.length = sizeof(*sccb); sccb->header.function_code = 0x80; sccb->header.control_mask[2] = 0x80; - rc = sclp_cmd_sync_early(commands[i], sccb); + rc = sclp_early_cmd_sync(commands[i], sccb); } while (rc == -EBUSY); if (rc) @@ -104,12 +85,12 @@ static int __init sclp_read_info_early(struct read_info_sccb *sccb) return -EIO; } -static void __init sclp_facilities_detect(struct read_info_sccb *sccb) +static void __init sclp_early_facilities_detect(struct read_info_sccb *sccb) { struct sclp_core_entry *cpue; u16 boot_cpu_address, cpu; - if (sclp_read_info_early(sccb)) + if (sclp_early_read_info(sccb)) return; sclp.facilities = sccb->facilities; @@ -172,59 +153,19 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb) } /* - * This function will be called after sclp_facilities_detect(), which gets - * called from early.c code. The sclp_facilities_detect() function retrieves + * This function will be called after sclp_early_facilities_detect(), which gets + * called from early.c code. The sclp_early_facilities_detect() function retrieves * and saves the IPL information. */ -void __init sclp_get_ipl_info(struct sclp_ipl_info *info) +void __init sclp_early_get_ipl_info(struct sclp_ipl_info *info) { *info = sclp_ipl_info; } -static int __init sclp_cmd_early(sclp_cmdw_t cmd, void *sccb) -{ - int rc; +static struct sclp_core_info sclp_early_core_info __initdata; +static int sclp_early_core_info_valid __initdata; - do { - rc = sclp_cmd_sync_early(cmd, sccb); - } while (rc == -EBUSY); - - if (rc) - return -EIO; - if (((struct sccb_header *) sccb)->response_code != 0x0020) - return -EIO; - return 0; -} - -static void __init sccb_init_eq_size(struct sdias_sccb *sccb) -{ - memset(sccb, 0, sizeof(*sccb)); - - sccb->hdr.length = sizeof(*sccb); - sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf); - sccb->evbuf.hdr.type = EVTYP_SDIAS; - sccb->evbuf.event_qual = SDIAS_EQ_SIZE; - sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP; - sccb->evbuf.event_id = 4712; - sccb->evbuf.dbs = 1; -} - -static int __init sclp_set_event_mask(struct init_sccb *sccb, - unsigned long receive_mask, - unsigned long send_mask) -{ - memset(sccb, 0, sizeof(*sccb)); - sccb->header.length = sizeof(*sccb); - sccb->mask_length = sizeof(sccb_mask_t); - sccb->receive_mask = receive_mask; - sccb->send_mask = send_mask; - return sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_MASK, sccb); -} - -static struct sclp_core_info sclp_core_info_early __initdata; -static int sclp_core_info_early_valid __initdata; - -static void __init sclp_init_core_info_early(struct read_cpu_info_sccb *sccb) +static void __init sclp_early_init_core_info(struct read_cpu_info_sccb *sccb) { int rc; @@ -233,80 +174,76 @@ static void __init sclp_init_core_info_early(struct read_cpu_info_sccb *sccb) memset(sccb, 0, sizeof(*sccb)); sccb->header.length = sizeof(*sccb); do { - rc = sclp_cmd_sync_early(SCLP_CMDW_READ_CPU_INFO, sccb); + rc = sclp_early_cmd_sync(SCLP_CMDW_READ_CPU_INFO, sccb); } while (rc == -EBUSY); if (rc) return; if (sccb->header.response_code != 0x0010) return; - sclp_fill_core_info(&sclp_core_info_early, sccb); - sclp_core_info_early_valid = 1; + sclp_fill_core_info(&sclp_early_core_info, sccb); + sclp_early_core_info_valid = 1; } -int __init _sclp_get_core_info_early(struct sclp_core_info *info) +int __init sclp_early_get_core_info(struct sclp_core_info *info) { - if (!sclp_core_info_early_valid) + if (!sclp_early_core_info_valid) return -EIO; - *info = sclp_core_info_early; + *info = sclp_early_core_info; return 0; } -static long __init sclp_hsa_size_init(struct sdias_sccb *sccb) +static long __init sclp_early_hsa_size_init(struct sdias_sccb *sccb) { - sccb_init_eq_size(sccb); - if (sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_DATA, sccb)) + memset(sccb, 0, sizeof(*sccb)); + sccb->hdr.length = sizeof(*sccb); + sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf); + sccb->evbuf.hdr.type = EVTYP_SDIAS; + sccb->evbuf.event_qual = SDIAS_EQ_SIZE; + sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP; + sccb->evbuf.event_id = 4712; + sccb->evbuf.dbs = 1; + if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb)) return -EIO; if (sccb->evbuf.blk_cnt == 0) return 0; return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE; } -static long __init sclp_hsa_copy_wait(struct sccb_header *sccb) +static long __init sclp_early_hsa_copy_wait(struct sdias_sccb *sccb) { memset(sccb, 0, PAGE_SIZE); - sccb->length = PAGE_SIZE; - if (sclp_cmd_early(SCLP_CMDW_READ_EVENT_DATA, sccb)) + sccb->hdr.length = PAGE_SIZE; + if (sclp_early_cmd(SCLP_CMDW_READ_EVENT_DATA, sccb)) return -EIO; - if (((struct sdias_sccb *) sccb)->evbuf.blk_cnt == 0) + if (sccb->evbuf.blk_cnt == 0) return 0; - return (((struct sdias_sccb *) sccb)->evbuf.blk_cnt - 1) * PAGE_SIZE; + return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE; } -static void __init sclp_hsa_size_detect(void *sccb) +static void __init sclp_early_hsa_size_detect(void *sccb) { - long size; + unsigned long flags; + long size = -EIO; - /* First try synchronous interface (LPAR) */ - if (sclp_set_event_mask(sccb, 0, 0x40000010)) - return; - size = sclp_hsa_size_init(sccb); - if (size < 0) - return; - if (size != 0) + raw_local_irq_save(flags); + if (sclp_early_set_event_mask(sccb, EVTYP_SDIAS_MASK, EVTYP_SDIAS_MASK)) goto out; - /* Then try asynchronous interface (z/VM) */ - if (sclp_set_event_mask(sccb, 0x00000010, 0x40000010)) - return; - size = sclp_hsa_size_init(sccb); - if (size < 0) - return; - size = sclp_hsa_copy_wait(sccb); - if (size < 0) - return; + size = sclp_early_hsa_size_init(sccb); + /* First check for synchronous response (LPAR) */ + if (size) + goto out_mask; + if (!(S390_lowcore.ext_params & 1)) + sclp_early_wait_irq(); + size = sclp_early_hsa_copy_wait(sccb); +out_mask: + sclp_early_set_event_mask(sccb, 0, 0); out: - sclp.hsa_size = size; -} - -static unsigned int __init sclp_con_check_linemode(struct init_sccb *sccb) -{ - if (!(sccb->sclp_send_mask & EVTYP_OPCMD_MASK)) - return 0; - if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK))) - return 0; - return 1; + raw_local_irq_restore(flags); + if (size > 0) + sclp.hsa_size = size; } -static void __init sclp_console_detect(struct init_sccb *sccb) +static void __init sclp_early_console_detect(struct init_sccb *sccb) { if (sccb->header.response_code != 0x20) return; @@ -314,21 +251,22 @@ static void __init sclp_console_detect(struct init_sccb *sccb) if (sccb->sclp_send_mask & EVTYP_VT220MSG_MASK) sclp.has_vt220 = 1; - if (sclp_con_check_linemode(sccb)) + if (sclp_early_con_check_linemode(sccb)) sclp.has_linemode = 1; } void __init sclp_early_detect(void) { - void *sccb = &sccb_early; + void *sccb = &sclp_early_sccb; - sclp_facilities_detect(sccb); - sclp_init_core_info_early(sccb); - sclp_hsa_size_detect(sccb); + sclp_early_facilities_detect(sccb); + sclp_early_init_core_info(sccb); + sclp_early_hsa_size_detect(sccb); - /* Turn off SCLP event notifications. Also save remote masks in the + /* + * Turn off SCLP event notifications. Also save remote masks in the * sccb. These are sufficient to detect sclp console capabilities. */ - sclp_set_event_mask(sccb, 0, 0); - sclp_console_detect(sccb); + sclp_early_set_event_mask(sccb, 0, 0); + sclp_early_console_detect(sccb); } diff --git a/drivers/s390/char/sclp_early_core.c b/drivers/s390/char/sclp_early_core.c index 2723ab56fb8f..97f4c84d6635 100644 --- a/drivers/s390/char/sclp_early_core.c +++ b/drivers/s390/char/sclp_early_core.c @@ -2,29 +2,24 @@ * Copyright IBM Corp. 2015 * Author(s): Martin Schwidefsky */ + #include +#include +#include #include #include -#include -#include -#include - -#define EVTYP_VT220MSG_MASK 0x00000040 -#define EVTYP_MSG_MASK 0x40000000 - -static char _sclp_work_area[4096] __aligned(PAGE_SIZE) __section(data); -static bool have_vt220 __section(data); -static bool have_linemode __section(data); +#include "sclp.h" +#include "sclp_rw.h" +char sclp_early_sccb[PAGE_SIZE] __aligned(PAGE_SIZE) __section(data); int sclp_init_state __section(data) = sclp_init_state_uninitialized; -static void _sclp_wait_int(void) +void sclp_early_wait_irq(void) { - unsigned long psw_mask, addr, flags; + unsigned long psw_mask, addr; psw_t psw_ext_save, psw_wait; union ctlreg0 cr0, cr0_new; - raw_local_irq_save(flags); __ctl_store(cr0.val, 0, 0); cr0_new.val = cr0.val & ~CR0_IRQ_SUBCLASS_MASK; cr0_new.lap = 0; @@ -53,165 +48,173 @@ static void _sclp_wait_int(void) S390_lowcore.external_new_psw = psw_ext_save; __ctl_load(cr0.val, 0, 0); - raw_local_irq_restore(flags); } -static int _sclp_servc(unsigned int cmd, char *sccb) +int sclp_early_cmd_sync(sclp_cmdw_t cmd, void *sccb) { - unsigned int cc; + unsigned long flags; + int rc; - do { - asm volatile( - " .insn rre,0xb2200000,%1,%2\n" - " ipm %0\n" - : "=d" (cc) : "d" (cmd), "a" (sccb) - : "cc", "memory"); - cc >>= 28; - if (cc == 3) - return -EINVAL; - _sclp_wait_int(); - } while (cc != 0); - return (*(unsigned short *)(sccb + 6) == 0x20) ? 0 : -EIO; + raw_local_irq_save(flags); + rc = sclp_service_call(cmd, sccb); + if (rc) + goto out; + sclp_early_wait_irq(); +out: + raw_local_irq_restore(flags); + return rc; } -static int _sclp_setup(int disable) +int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb) { - static unsigned char init_sccb[] = { - 0x00, 0x1c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x04, - 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - unsigned int *masks; int rc; - memcpy(_sclp_work_area, init_sccb, 28); - masks = (unsigned int *)(_sclp_work_area + 12); - if (disable) - memset(masks, 0, 16); - /* SCLP write mask */ - rc = _sclp_servc(0x00780005, _sclp_work_area); + do { + rc = sclp_early_cmd_sync(cmd, sccb); + } while (rc == -EBUSY); if (rc) - return rc; - have_vt220 = masks[2] & EVTYP_VT220MSG_MASK; - have_linemode = masks[2] & EVTYP_MSG_MASK; + return -EIO; + if (((struct sccb_header *) sccb)->response_code != 0x0020) + return -EIO; return 0; } +struct write_sccb { + struct sccb_header header; + struct msg_buf msg; +} __packed; + /* Output multi-line text using SCLP Message interface. */ -static void _sclp_print_lm(const char *str, unsigned int len) +static void sclp_early_print_lm(const char *str, unsigned int len) { - static unsigned char write_head[] = { - /* sccb header */ - 0x00, 0x52, /* 0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 2 */ - /* evbuf */ - 0x00, 0x4a, /* 8 */ - 0x02, 0x00, 0x00, 0x00, /* 10 */ - /* mdb */ - 0x00, 0x44, /* 14 */ - 0x00, 0x01, /* 16 */ - 0xd4, 0xc4, 0xc2, 0x40, /* 18 */ - 0x00, 0x00, 0x00, 0x01, /* 22 */ - /* go */ - 0x00, 0x38, /* 26 */ - 0x00, 0x01, /* 28 */ - 0x00, 0x00, 0x00, 0x00, /* 30 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 34 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 42 */ - 0x00, 0x00, 0x00, 0x00, /* 50 */ - 0x00, 0x00, /* 54 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 56 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 64 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 72 */ - 0x00, 0x00, /* 80 */ - }; - static unsigned char write_mto[] = { - /* mto */ - 0x00, 0x0a, /* 0 */ - 0x00, 0x04, /* 2 */ - 0x10, 0x00, /* 4 */ - 0x00, 0x00, 0x00, 0x00 /* 6 */ - }; - unsigned char *ptr, *end_ptr, ch; - unsigned int count, num; - - num = 0; - memcpy(_sclp_work_area, write_head, sizeof(write_head)); - ptr = _sclp_work_area + sizeof(write_head); - end_ptr = _sclp_work_area + sizeof(_sclp_work_area) - 1; + unsigned char *ptr, *end, ch; + unsigned int count, offset; + struct write_sccb *sccb; + struct msg_buf *msg; + struct mdb *mdb; + struct mto *mto; + struct go *go; + + sccb = (struct write_sccb *) &sclp_early_sccb; + end = (unsigned char *) sccb + sizeof(sclp_early_sccb) - 1; + memset(sccb, 0, sizeof(*sccb)); + ptr = (unsigned char *) &sccb->msg.mdb.mto; + offset = 0; do { - if (ptr + sizeof(write_mto) > end_ptr) - break; - memcpy(ptr, write_mto, sizeof(write_mto)); - for (count = sizeof(write_mto); num < len; count++) { - num++; - ch = *str++; - if (ch == 0x0a) - break; - if (ptr > end_ptr) + for (count = sizeof(*mto); offset < len; count++) { + ch = str[offset++]; + if ((ch == 0x0a) || (ptr + count > end)) break; ptr[count] = _ascebc[ch]; } - /* Update length fields in mto, mdb, evbuf and sccb */ - *(unsigned short *) ptr = count; - *(unsigned short *)(_sclp_work_area + 14) += count; - *(unsigned short *)(_sclp_work_area + 8) += count; - *(unsigned short *)(_sclp_work_area + 0) += count; + mto = (struct mto *) ptr; + memset(mto, 0, sizeof(*mto)); + mto->length = count; + mto->type = 4; + mto->line_type_flags = LNTPFLGS_ENDTEXT; ptr += count; - } while (num < len); - - /* SCLP write data */ - _sclp_servc(0x00760005, _sclp_work_area); + } while ((offset < len) && (ptr + sizeof(*mto) <= end)); + len = ptr - (unsigned char *) sccb; + sccb->header.length = len - offsetof(struct write_sccb, header); + msg = &sccb->msg; + msg->header.type = EVTYP_MSG; + msg->header.length = len - offsetof(struct write_sccb, msg.header); + mdb = &msg->mdb; + mdb->header.type = 1; + mdb->header.tag = 0xD4C4C240; + mdb->header.revision_code = 1; + mdb->header.length = len - offsetof(struct write_sccb, msg.mdb.header); + go = &mdb->go; + go->length = sizeof(*go); + go->type = 1; + sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb); } +struct vt220_sccb { + struct sccb_header header; + struct { + struct evbuf_header header; + char data[]; + } msg; +} __packed; + /* Output multi-line text (plus a newline) using SCLP VT220 * interface. */ -static void _sclp_print_vt220(const char *str, unsigned int len) +static void sclp_early_print_vt220(const char *str, unsigned int len) +{ + struct vt220_sccb *sccb; + + sccb = (struct vt220_sccb *) &sclp_early_sccb; + if (sizeof(*sccb) + len >= sizeof(sclp_early_sccb)) + len = sizeof(sclp_early_sccb) - sizeof(*sccb) - 1; + memset(sccb, 0, sizeof(*sccb)); + memcpy(&sccb->msg.data, str, len); + sccb->msg.data[len] = '\n'; + sccb->header.length = sizeof(*sccb) + len + 1; + sccb->msg.header.length = sizeof(sccb->msg) + len + 1; + sccb->msg.header.type = EVTYP_VT220MSG; + sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb); +} + +int sclp_early_set_event_mask(struct init_sccb *sccb, + unsigned long receive_mask, + unsigned long send_mask) +{ + memset(sccb, 0, sizeof(*sccb)); + sccb->header.length = sizeof(*sccb); + sccb->mask_length = sizeof(sccb_mask_t); + sccb->receive_mask = receive_mask; + sccb->send_mask = send_mask; + return sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb); +} + +unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb) { - static unsigned char const write_head[] = { - /* sccb header */ - 0x00, 0x0e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* evbuf header */ - 0x00, 0x06, - 0x1a, 0x00, 0x00, 0x00, - }; - - if (sizeof(write_head) + len >= sizeof(_sclp_work_area)) - len = sizeof(_sclp_work_area) - sizeof(write_head) - 1; - - memcpy(_sclp_work_area, write_head, sizeof(write_head)); - memcpy(_sclp_work_area + sizeof(write_head), str, len); - _sclp_work_area[sizeof(write_head) + len] = '\n'; - - /* Update length fields in evbuf and sccb headers */ - *(unsigned short *)(_sclp_work_area + 8) += len + 1; - *(unsigned short *)(_sclp_work_area + 0) += len + 1; - - /* SCLP write data */ - (void)_sclp_servc(0x00760005, _sclp_work_area); + if (!(sccb->sclp_send_mask & EVTYP_OPCMD_MASK)) + return 0; + if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK))) + return 0; + return 1; +} + +static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220) +{ + unsigned long receive_mask, send_mask; + struct init_sccb *sccb; + int rc; + + *have_linemode = *have_vt220 = 0; + sccb = (struct init_sccb *) &sclp_early_sccb; + receive_mask = disable ? 0 : EVTYP_OPCMD_MASK; + send_mask = disable ? 0 : EVTYP_VT220MSG_MASK | EVTYP_MSG_MASK; + rc = sclp_early_set_event_mask(sccb, receive_mask, send_mask); + if (rc) + return rc; + *have_linemode = sclp_early_con_check_linemode(sccb); + *have_vt220 = sccb->send_mask & EVTYP_VT220MSG_MASK; + return rc; } /* Output one or more lines of text on the SCLP console (VT220 and / * or line-mode). All lines get terminated; no need for a trailing LF. */ -void __sclp_print_early(const char *str, unsigned int len) +void __sclp_early_printk(const char *str, unsigned int len) { + int have_linemode, have_vt220; + if (sclp_init_state != sclp_init_state_uninitialized) return; - if (_sclp_setup(0) != 0) + if (sclp_early_setup(0, &have_linemode, &have_vt220) != 0) return; if (have_linemode) - _sclp_print_lm(str, len); + sclp_early_print_lm(str, len); if (have_vt220) - _sclp_print_vt220(str, len); - _sclp_setup(1); + sclp_early_print_vt220(str, len); + sclp_early_setup(1, &have_linemode, &have_vt220); } -void _sclp_print_early(const char *str) +void sclp_early_printk(const char *str) { - __sclp_print_early(str, strlen(str)); + __sclp_early_printk(str, strlen(str)); } -- cgit v1.2.3 From 02407baaebdef86571e4e939ddbd3b32d9b5d389 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 2 Feb 2017 10:33:20 +0100 Subject: s390/sclp: don't add new lines to each printed string The early vt220 sclp printk code added an extra new line to each printed multi-line text. If used for the early sclp console this will lead to numerous extra new lines. Therefore get rid of this semantic and require that each to be printed string contains a line feed character if a new line is wanted. Reviewed-by: Peter Oberparleiter Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/als.c | 7 +++++-- arch/s390/kernel/swsusp.S | 2 +- drivers/s390/char/sclp_early_core.c | 16 +++++++--------- 3 files changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers/s390') diff --git a/arch/s390/kernel/als.c b/arch/s390/kernel/als.c index 0b3a06f05f90..14769eb52a33 100644 --- a/arch/s390/kernel/als.c +++ b/arch/s390/kernel/als.c @@ -41,6 +41,7 @@ static void __init print_machine_type(void) get_cpu_id(&id); u16_to_hex(type_str, id.machine); strcat(mach_str, type_str); + strcat(mach_str, "\n"); sclp_early_printk(mach_str); } @@ -79,6 +80,7 @@ static void __init print_missing_facilities(void) * z/VM adds a four character prefix. */ if (strlen(als_str) > 70) { + strcat(als_str, "\n"); sclp_early_printk(als_str); *als_str = '\0'; } @@ -87,13 +89,14 @@ static void __init print_missing_facilities(void) first = 0; } } + strcat(als_str, "\n"); sclp_early_printk(als_str); - sclp_early_printk("See Principles of Operations for facility bits"); + sclp_early_printk("See Principles of Operations for facility bits\n"); } static void __init facility_mismatch(void) { - sclp_early_printk("The Linux kernel requires more recent processor hardware"); + sclp_early_printk("The Linux kernel requires more recent processor hardware\n"); print_machine_type(); print_missing_facilities(); disabled_wait(0x8badcccc); diff --git a/arch/s390/kernel/swsusp.S b/arch/s390/kernel/swsusp.S index 6c56fb89f553..6bebc935e9c2 100644 --- a/arch/s390/kernel/swsusp.S +++ b/arch/s390/kernel/swsusp.S @@ -273,7 +273,7 @@ restore_registers: .Ldisabled_wait_31: .long 0x000a0000,0x00000000 .Lpanic_string: - .asciz "Resume not possible because suspend CPU is no longer available" + .asciz "Resume not possible because suspend CPU is no longer available\n" .align 8 .Lrestart_diag308_psw: .long 0x00080000,0x80000000 diff --git a/drivers/s390/char/sclp_early_core.c b/drivers/s390/char/sclp_early_core.c index 97f4c84d6635..cc3ad8c69a7a 100644 --- a/drivers/s390/char/sclp_early_core.c +++ b/drivers/s390/char/sclp_early_core.c @@ -138,21 +138,18 @@ struct vt220_sccb { } msg; } __packed; -/* Output multi-line text (plus a newline) using SCLP VT220 - * interface. - */ +/* Output multi-line text using SCLP VT220 interface. */ static void sclp_early_print_vt220(const char *str, unsigned int len) { struct vt220_sccb *sccb; sccb = (struct vt220_sccb *) &sclp_early_sccb; if (sizeof(*sccb) + len >= sizeof(sclp_early_sccb)) - len = sizeof(sclp_early_sccb) - sizeof(*sccb) - 1; + len = sizeof(sclp_early_sccb) - sizeof(*sccb); memset(sccb, 0, sizeof(*sccb)); memcpy(&sccb->msg.data, str, len); - sccb->msg.data[len] = '\n'; - sccb->header.length = sizeof(*sccb) + len + 1; - sccb->msg.header.length = sizeof(sccb->msg) + len + 1; + sccb->header.length = sizeof(*sccb) + len; + sccb->msg.header.length = sizeof(sccb->msg) + len; sccb->msg.header.type = EVTYP_VT220MSG; sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb); } @@ -196,8 +193,9 @@ static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220) return rc; } -/* Output one or more lines of text on the SCLP console (VT220 and / - * or line-mode). All lines get terminated; no need for a trailing LF. +/* + * Output one or more lines of text on the SCLP console (VT220 and / + * or line-mode). */ void __sclp_early_printk(const char *str, unsigned int len) { -- cgit v1.2.3 From f694bb3a36a8113addd31817e4b5aca42326353d Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 2 Feb 2017 12:33:03 +0100 Subject: s390/sclp: get rid of common response code handling Get rid of common response code handling. Each command requires its own response code handling anyway. Also the retry in case of -EBUSY does not work and can be simply removed. Reviewed-by: Peter Oberparleiter Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/char/sclp.h | 1 - drivers/s390/char/sclp_early.c | 27 +++++++++++---------------- drivers/s390/char/sclp_early_core.c | 22 ++++++---------------- 3 files changed, 17 insertions(+), 33 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index 78d5f542d979..53b5d1b9761a 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -224,7 +224,6 @@ extern unsigned long sclp_console_full; extern char sclp_early_sccb[PAGE_SIZE]; void sclp_early_wait_irq(void); -int sclp_early_cmd_sync(sclp_cmdw_t cmd, void *sccb); int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb); unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb); int sclp_early_set_event_mask(struct init_sccb *sccb, diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c index 2f9e50379e64..519ec1787117 100644 --- a/drivers/s390/char/sclp_early.c +++ b/drivers/s390/char/sclp_early.c @@ -62,20 +62,16 @@ EXPORT_SYMBOL(sclp); static int __init sclp_early_read_info(struct read_info_sccb *sccb) { - int rc, i; + int i; sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED, SCLP_CMDW_READ_SCP_INFO}; for (i = 0; i < ARRAY_SIZE(commands); i++) { - do { - memset(sccb, 0, sizeof(*sccb)); - sccb->header.length = sizeof(*sccb); - sccb->header.function_code = 0x80; - sccb->header.control_mask[2] = 0x80; - rc = sclp_early_cmd_sync(commands[i], sccb); - } while (rc == -EBUSY); - - if (rc) + memset(sccb, 0, sizeof(*sccb)); + sccb->header.length = sizeof(*sccb); + sccb->header.function_code = 0x80; + sccb->header.control_mask[2] = 0x80; + if (sclp_early_cmd(commands[i], sccb)) break; if (sccb->header.response_code == 0x10) return 0; @@ -167,16 +163,11 @@ static int sclp_early_core_info_valid __initdata; static void __init sclp_early_init_core_info(struct read_cpu_info_sccb *sccb) { - int rc; - if (!SCLP_HAS_CPU_INFO) return; memset(sccb, 0, sizeof(*sccb)); sccb->header.length = sizeof(*sccb); - do { - rc = sclp_early_cmd_sync(SCLP_CMDW_READ_CPU_INFO, sccb); - } while (rc == -EBUSY); - if (rc) + if (sclp_early_cmd(SCLP_CMDW_READ_CPU_INFO, sccb)) return; if (sccb->header.response_code != 0x0010) return; @@ -204,6 +195,8 @@ static long __init sclp_early_hsa_size_init(struct sdias_sccb *sccb) sccb->evbuf.dbs = 1; if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb)) return -EIO; + if (sccb->hdr.response_code != 0x20) + return -EIO; if (sccb->evbuf.blk_cnt == 0) return 0; return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE; @@ -215,6 +208,8 @@ static long __init sclp_early_hsa_copy_wait(struct sdias_sccb *sccb) sccb->hdr.length = PAGE_SIZE; if (sclp_early_cmd(SCLP_CMDW_READ_EVENT_DATA, sccb)) return -EIO; + if ((sccb->hdr.response_code != 0x20) && (sccb->hdr.response_code != 0x220)) + return -EIO; if (sccb->evbuf.blk_cnt == 0) return 0; return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE; diff --git a/drivers/s390/char/sclp_early_core.c b/drivers/s390/char/sclp_early_core.c index cc3ad8c69a7a..5029cc87e80f 100644 --- a/drivers/s390/char/sclp_early_core.c +++ b/drivers/s390/char/sclp_early_core.c @@ -50,7 +50,7 @@ void sclp_early_wait_irq(void) __ctl_load(cr0.val, 0, 0); } -int sclp_early_cmd_sync(sclp_cmdw_t cmd, void *sccb) +int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb) { unsigned long flags; int rc; @@ -65,20 +65,6 @@ out: return rc; } -int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb) -{ - int rc; - - do { - rc = sclp_early_cmd_sync(cmd, sccb); - } while (rc == -EBUSY); - if (rc) - return -EIO; - if (((struct sccb_header *) sccb)->response_code != 0x0020) - return -EIO; - return 0; -} - struct write_sccb { struct sccb_header header; struct msg_buf msg; @@ -163,7 +149,11 @@ int sclp_early_set_event_mask(struct init_sccb *sccb, sccb->mask_length = sizeof(sccb_mask_t); sccb->receive_mask = receive_mask; sccb->send_mask = send_mask; - return sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb); + if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb)) + return -EIO; + if (sccb->header.response_code != 0x20) + return -EIO; + return 0; } unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb) -- cgit v1.2.3 From 2dfa6688aafdc3f74efeb1cf05fb871465d67f79 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Wed, 8 Feb 2017 15:34:22 +0100 Subject: scsi: zfcp: fix use-after-free by not tracing WKA port open/close on failed send Dan Carpenter kindly reported: The patch d27a7cb91960: "zfcp: trace on request for open and close of WKA port" from Aug 10, 2016, leads to the following static checker warning: drivers/s390/scsi/zfcp_fsf.c:1615 zfcp_fsf_open_wka_port() warn: 'req' was already freed. drivers/s390/scsi/zfcp_fsf.c 1609 zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT); 1610 retval = zfcp_fsf_req_send(req); 1611 if (retval) 1612 zfcp_fsf_req_free(req); ^^^ Freed. 1613 out: 1614 spin_unlock_irq(&qdio->req_q_lock); 1615 if (req && !IS_ERR(req)) 1616 zfcp_dbf_rec_run_wka("fsowp_1", wka_port, req->req_id); ^^^^^^^^^^^ Use after free. 1617 return retval; 1618 } Same thing for zfcp_fsf_close_wka_port() as well. Rather than relying on req being NULL (or ERR_PTR) for all cases where we don't want to trace or should not trace, simply check retval which is unconditionally initialized with -EIO != 0 and it can only become 0 on successful retval = zfcp_fsf_req_send(req). With that we can also remove the then again unnecessary unconditional initialization of req which was introduced with that earlier commit. Reported-by: Dan Carpenter Suggested-by: Benjamin Block Signed-off-by: Steffen Maier Fixes: d27a7cb91960 ("zfcp: trace on request for open and close of WKA port") Cc: #2.6.38+ Reviewed-by: Benjamin Block Reviewed-by: Jens Remus Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_fsf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 75f820ca17b7..27ff38f839fc 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -1583,7 +1583,7 @@ out: int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port) { struct zfcp_qdio *qdio = wka_port->adapter->qdio; - struct zfcp_fsf_req *req = NULL; + struct zfcp_fsf_req *req; int retval = -EIO; spin_lock_irq(&qdio->req_q_lock); @@ -1612,7 +1612,7 @@ int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port) zfcp_fsf_req_free(req); out: spin_unlock_irq(&qdio->req_q_lock); - if (req && !IS_ERR(req)) + if (!retval) zfcp_dbf_rec_run_wka("fsowp_1", wka_port, req->req_id); return retval; } @@ -1638,7 +1638,7 @@ static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req) int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port) { struct zfcp_qdio *qdio = wka_port->adapter->qdio; - struct zfcp_fsf_req *req = NULL; + struct zfcp_fsf_req *req; int retval = -EIO; spin_lock_irq(&qdio->req_q_lock); @@ -1667,7 +1667,7 @@ int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port) zfcp_fsf_req_free(req); out: spin_unlock_irq(&qdio->req_q_lock); - if (req && !IS_ERR(req)) + if (!retval) zfcp_dbf_rec_run_wka("fscwp_1", wka_port, req->req_id); return retval; } -- cgit v1.2.3 From 260021e21b8fcc375984ac7733f93509bf9ad100 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Fri, 17 Feb 2017 08:12:10 +0100 Subject: s390/zcrypt: Removed unneeded debug feature directory creation. The ap bus code and the zcrypt api had invocations to the debug feature debugfs_create_dir() call but never populated these directories in any way. Removed this unneeded code. Signed-off-by: Harald Freudenberger Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 3 --- drivers/s390/crypto/zcrypt_api.c | 3 --- 2 files changed, 6 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 5fa699192864..5a3a18f7b896 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -86,7 +86,6 @@ static bool initialised; /* * AP bus related debug feature things. */ -static struct dentry *ap_dbf_root; debug_info_t *ap_dbf_info; /* @@ -1148,7 +1147,6 @@ static struct reset_call ap_reset_call = { int __init ap_debug_init(void) { - ap_dbf_root = debugfs_create_dir("ap", NULL); ap_dbf_info = debug_register("ap", 1, 1, DBF_MAX_SPRINTF_ARGS * sizeof(long)); debug_register_view(ap_dbf_info, &debug_sprintf_view); @@ -1159,7 +1157,6 @@ int __init ap_debug_init(void) void ap_debug_exit(void) { - debugfs_remove(ap_dbf_root); debug_unregister(ap_dbf_info); } diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 926169c3d9b9..144a17941e6f 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -81,7 +81,6 @@ EXPORT_SYMBOL(zcrypt_rescan_req); static LIST_HEAD(zcrypt_ops_list); /* Zcrypt related debug feature stuff. */ -static struct dentry *zcrypt_dbf_root; debug_info_t *zcrypt_dbf_info; /** @@ -1427,7 +1426,6 @@ void zcrypt_rng_device_remove(void) int __init zcrypt_debug_init(void) { - zcrypt_dbf_root = debugfs_create_dir("zcrypt", NULL); zcrypt_dbf_info = debug_register("zcrypt", 1, 1, DBF_MAX_SPRINTF_ARGS * sizeof(long)); debug_register_view(zcrypt_dbf_info, &debug_sprintf_view); @@ -1438,7 +1436,6 @@ int __init zcrypt_debug_init(void) void zcrypt_debug_exit(void) { - debugfs_remove(zcrypt_dbf_root); debug_unregister(zcrypt_dbf_info); } -- cgit v1.2.3 From 50a0d46c98b72cde3c6945f066c0adf31e4e8590 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 9 Feb 2017 09:48:10 -0500 Subject: s390/zcrypt: make ap_bus explicitly non-modular The Makefile in drivers/s390 has: obj-y += cio/ block/ char/ crypto/ net/ scsi/ virtio/ and the Makefile in crypto/ has: ap-objs := ap_bus.o ap_card.o ap_queue.o meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. Since module_init translates to device_initcall in the non-modular case, the init ordering remains unchanged with this commit. Also note that MODULE_ALIAS is a no-op for non-module builds. We also delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. We replace module.h with moduleparam.h since the file does declare some module parameters even though it is not modular itself. Signed-off-by: Paul Gortmaker Signed-off-by: Harald Freudenberger Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 54 +++----------------------------------------- 1 file changed, 3 insertions(+), 51 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 5a3a18f7b896..56db76c05775 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -27,7 +27,7 @@ #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt #include -#include +#include #include #include #include @@ -54,16 +54,7 @@ #include "ap_debug.h" /* - * Module description. - */ -MODULE_AUTHOR("IBM Corporation"); -MODULE_DESCRIPTION("Adjunct Processor Bus driver, " \ - "Copyright IBM Corp. 2006, 2012"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CRYPTO("z90crypt"); - -/* - * Module parameter + * Module parameters; note though this file itself isn't modular. */ int ap_domain_index = -1; /* Adjunct Processor Domain Index */ static DEFINE_SPINLOCK(ap_domain_lock); @@ -1267,43 +1258,4 @@ out_free: kfree(ap_configuration); return rc; } - -/** - * ap_modules_exit(): The module termination code - * - * Terminates the module. - */ -void ap_module_exit(void) -{ - int i; - - initialised = false; - ap_reset_domain(); - ap_poll_thread_stop(); - del_timer_sync(&ap_config_timer); - hrtimer_cancel(&ap_poll_timer); - tasklet_kill(&ap_tasklet); - - /* first remove queue devices */ - bus_for_each_dev(&ap_bus_type, NULL, NULL, - __ap_queue_devices_unregister); - /* now remove the card devices */ - bus_for_each_dev(&ap_bus_type, NULL, NULL, - __ap_card_devices_unregister); - - /* remove bus attributes */ - for (i = 0; ap_bus_attrs[i]; i++) - bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); - unregister_pm_notifier(&ap_power_notifier); - root_device_unregister(ap_root_device); - bus_unregister(&ap_bus_type); - kfree(ap_configuration); - unregister_reset_call(&ap_reset_call); - if (ap_using_interrupts()) - unregister_adapter_interrupt(&ap_airq); - - ap_debug_exit(); -} - -module_init(ap_module_init); -module_exit(ap_module_exit); +device_initcall(ap_module_init); -- cgit v1.2.3 From 77759137248f34864a8f7a58bbcebfcf1047504a Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 20 Feb 2017 14:52:58 +0100 Subject: s390/chsc: Add exception handler for CHSC instruction Prevent kernel crashes due to unhandled exceptions raised by the CHSC instruction which may for example be triggered by invalid ioctl data. Fixes: 64150adf89df ("s390/cio: Introduce generic synchronous CHSC IOCTL") Cc: # v3.11+ Signed-off-by: Peter Oberparleiter Reviewed-by: Sebastian Ott Reviewed-by: Cornelia Huck Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/ioasm.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/cio/ioasm.c b/drivers/s390/cio/ioasm.c index 8225da619014..4182f60124da 100644 --- a/drivers/s390/cio/ioasm.c +++ b/drivers/s390/cio/ioasm.c @@ -165,13 +165,15 @@ int tpi(struct tpi_info *addr) int chsc(void *chsc_area) { typedef struct { char _[4096]; } addr_type; - int cc; + int cc = -EIO; asm volatile( " .insn rre,0xb25f0000,%2,0\n" - " ipm %0\n" + "0: ipm %0\n" " srl %0,28\n" - : "=d" (cc), "=m" (*(addr_type *) chsc_area) + "1:\n" + EX_TABLE(0b, 1b) + : "+d" (cc), "=m" (*(addr_type *) chsc_area) : "d" (chsc_area), "m" (*(addr_type *) chsc_area) : "cc"); trace_s390_cio_chsc(chsc_area, cc); -- cgit v1.2.3 From d0360d7b523f150c2ff0ab3c9361dfc3b4185cbf Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Tue, 15 Nov 2016 09:05:00 +0100 Subject: s390/zcrypt: Enable request count reset for cards and queues. This patch introduces the possibility to reset the request_count attribute for cards and queues to zero. This can be used to set a clear state on the counters before running an application and try out if and which hardware is actually used. If the request_count counter of a card is reset, for all associated queues the request_count is also zeroed. If just a queue request_count is reset the card counter is not updated however. Signed-off-by: Harald Freudenberger Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_card.c | 24 ++++++++++++++++++++---- drivers/s390/crypto/ap_queue.c | 21 +++++++++++++++++---- 2 files changed, 37 insertions(+), 8 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/ap_card.c b/drivers/s390/crypto/ap_card.c index 1cd9128593e4..cfa161ccc74e 100644 --- a/drivers/s390/crypto/ap_card.c +++ b/drivers/s390/crypto/ap_card.c @@ -58,9 +58,9 @@ static ssize_t ap_functions_show(struct device *dev, static DEVICE_ATTR(ap_functions, 0444, ap_functions_show, NULL); -static ssize_t ap_request_count_show(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t ap_req_count_show(struct device *dev, + struct device_attribute *attr, + char *buf) { struct ap_card *ac = to_ap_card(dev); unsigned int req_cnt; @@ -72,7 +72,23 @@ static ssize_t ap_request_count_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", req_cnt); } -static DEVICE_ATTR(request_count, 0444, ap_request_count_show, NULL); +static ssize_t ap_req_count_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ap_card *ac = to_ap_card(dev); + struct ap_queue *aq; + + spin_lock_bh(&ap_list_lock); + for_each_ap_queue(aq, ac) + aq->total_request_count = 0; + spin_unlock_bh(&ap_list_lock); + atomic_set(&ac->total_request_count, 0); + + return count; +} + +static DEVICE_ATTR(request_count, 0644, ap_req_count_show, ap_req_count_store); static ssize_t ap_requestq_count_show(struct device *dev, struct device_attribute *attr, char *buf) diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c index 7be67fa9f224..480c58a63769 100644 --- a/drivers/s390/crypto/ap_queue.c +++ b/drivers/s390/crypto/ap_queue.c @@ -459,9 +459,9 @@ EXPORT_SYMBOL(ap_queue_resume); /* * AP queue related attributes. */ -static ssize_t ap_request_count_show(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t ap_req_count_show(struct device *dev, + struct device_attribute *attr, + char *buf) { struct ap_queue *aq = to_ap_queue(dev); unsigned int req_cnt; @@ -472,7 +472,20 @@ static ssize_t ap_request_count_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", req_cnt); } -static DEVICE_ATTR(request_count, 0444, ap_request_count_show, NULL); +static ssize_t ap_req_count_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ap_queue *aq = to_ap_queue(dev); + + spin_lock_bh(&aq->lock); + aq->total_request_count = 0; + spin_unlock_bh(&aq->lock); + + return count; +} + +static DEVICE_ATTR(request_count, 0644, ap_req_count_show, ap_req_count_store); static ssize_t ap_requestq_count_show(struct device *dev, struct device_attribute *attr, char *buf) -- cgit v1.2.3 From f546d6a941912e0ff1d2d7f37f302884f3accd12 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Mon, 20 Feb 2017 15:32:36 +0100 Subject: s390/zcrypt: Cleanup leftover module code. The AP bus code is not buildable as kernel module any more. Commit 5fe38260d083 ("s390/zcrypt: make ap_bus explicitly non-modular") leaves one now unused function which gets removed with this patch. Signed-off-by: Harald Freudenberger Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 56db76c05775..9be4596d8a08 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1107,16 +1107,6 @@ static void ap_config_timeout(unsigned long ptr) queue_work(system_long_wq, &ap_scan_work); } -static void ap_reset_domain(void) -{ - int i; - - if (ap_domain_index == -1 || !ap_test_config_domain(ap_domain_index)) - return; - for (i = 0; i < AP_DEVICES; i++) - ap_rapq(AP_MKQID(i, ap_domain_index)); -} - static void ap_reset_all(void) { int i, j; -- cgit v1.2.3 From a1d001e26d5386c934345dc91f16b530e352f8d7 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Wed, 2 Nov 2016 14:32:32 +0100 Subject: s390/zcrypt: export additional symbols Export the two zcrypt device driver functions zcrypt_send_cprb and zcrypt_device_status_mask to be useable for other kernel code. Signed-off-by: Harald Freudenberger Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/zcrypt_api.c | 5 +++-- drivers/s390/crypto/zcrypt_api.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 144a17941e6f..93015f85d4a6 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -374,7 +374,7 @@ out: return rc; } -static long zcrypt_send_cprb(struct ica_xcRB *xcRB) +long zcrypt_send_cprb(struct ica_xcRB *xcRB) { struct zcrypt_card *zc, *pref_zc; struct zcrypt_queue *zq, *pref_zq; @@ -444,6 +444,7 @@ out: AP_QID_CARD(qid), AP_QID_QUEUE(qid)); return rc; } +EXPORT_SYMBOL(zcrypt_send_cprb); static bool is_desired_ep11_card(unsigned int dev_id, unsigned short target_num, @@ -619,7 +620,7 @@ out: return rc; } -static void zcrypt_device_status_mask(struct zcrypt_device_matrix *matrix) +void zcrypt_device_status_mask(struct zcrypt_device_matrix *matrix) { struct zcrypt_card *zc; struct zcrypt_queue *zq; diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h index 274a59051534..6c94efd23eac 100644 --- a/drivers/s390/crypto/zcrypt_api.h +++ b/drivers/s390/crypto/zcrypt_api.h @@ -190,5 +190,7 @@ void zcrypt_msgtype_unregister(struct zcrypt_ops *); struct zcrypt_ops *zcrypt_msgtype(unsigned char *, int); int zcrypt_api_init(void); void zcrypt_api_exit(void); +long zcrypt_send_cprb(struct ica_xcRB *xcRB); +void zcrypt_device_status_mask(struct zcrypt_device_matrix *devstatus); #endif /* _ZCRYPT_API_H_ */ -- cgit v1.2.3 From e80d4af0a320972aac58e2004d0ba4e44ef4c5c7 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Wed, 2 Nov 2016 14:37:20 +0100 Subject: s390/pkey: Introduce pkey kernel module This patch introcudes a new kernel module pkey which is providing protected key handling and management functions. The pkey API is available within the kernel for other s390 specific code to create and manage protected keys. Additionally the functions are exported to user space via IOCTL calls. The implementation makes extensive use of functions provided by the zcrypt device driver. For generating protected keys from secure keys there is also a CEX coprocessor card needed. Signed-off-by: Harald Freudenberger Signed-off-by: Martin Schwidefsky --- arch/s390/configs/default_defconfig | 1 + arch/s390/configs/performance_defconfig | 1 + arch/s390/defconfig | 1 + arch/s390/include/asm/pkey.h | 90 +++ arch/s390/include/uapi/asm/Kbuild | 1 + arch/s390/include/uapi/asm/pkey.h | 112 +++ drivers/crypto/Kconfig | 16 + drivers/s390/crypto/Makefile | 4 + drivers/s390/crypto/pkey_api.c | 1148 +++++++++++++++++++++++++++++++ 9 files changed, 1374 insertions(+) create mode 100644 arch/s390/include/asm/pkey.h create mode 100644 arch/s390/include/uapi/asm/pkey.h create mode 100644 drivers/s390/crypto/pkey_api.c (limited to 'drivers/s390') diff --git a/arch/s390/configs/default_defconfig b/arch/s390/configs/default_defconfig index e00975361fec..143b1e00b818 100644 --- a/arch/s390/configs/default_defconfig +++ b/arch/s390/configs/default_defconfig @@ -678,6 +678,7 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m CONFIG_ZCRYPT=m +CONFIG_PKEY=m CONFIG_CRYPTO_SHA1_S390=m CONFIG_CRYPTO_SHA256_S390=m CONFIG_CRYPTO_SHA512_S390=m diff --git a/arch/s390/configs/performance_defconfig b/arch/s390/configs/performance_defconfig index 2cf87343b590..2358bf33c5ef 100644 --- a/arch/s390/configs/performance_defconfig +++ b/arch/s390/configs/performance_defconfig @@ -628,6 +628,7 @@ CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m CONFIG_ZCRYPT=m +CONFIG_PKEY=m CONFIG_CRYPTO_SHA1_S390=m CONFIG_CRYPTO_SHA256_S390=m CONFIG_CRYPTO_SHA512_S390=m diff --git a/arch/s390/defconfig b/arch/s390/defconfig index d00e368fb5e6..68bfd09f1b02 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig @@ -229,6 +229,7 @@ CONFIG_CRYPTO_USER_API_HASH=m CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_ZCRYPT=m +CONFIG_PKEY=m CONFIG_CRYPTO_SHA1_S390=m CONFIG_CRYPTO_SHA256_S390=m CONFIG_CRYPTO_SHA512_S390=m diff --git a/arch/s390/include/asm/pkey.h b/arch/s390/include/asm/pkey.h new file mode 100644 index 000000000000..b48aef4188f6 --- /dev/null +++ b/arch/s390/include/asm/pkey.h @@ -0,0 +1,90 @@ +/* + * Kernelspace interface to the pkey device driver + * + * Copyright IBM Corp. 2016 + * + * Author: Harald Freudenberger + * + */ + +#ifndef _KAPI_PKEY_H +#define _KAPI_PKEY_H + +#include +#include +#include + +/* + * Generate (AES) random secure key. + * @param cardnr may be -1 (use default card) + * @param domain may be -1 (use default domain) + * @param keytype one of the PKEY_KEYTYPE values + * @param seckey pointer to buffer receiving the secure key + * @return 0 on success, negative errno value on failure + */ +int pkey_genseckey(__u16 cardnr, __u16 domain, + __u32 keytype, struct pkey_seckey *seckey); + +/* + * Generate (AES) secure key with given key value. + * @param cardnr may be -1 (use default card) + * @param domain may be -1 (use default domain) + * @param keytype one of the PKEY_KEYTYPE values + * @param clrkey pointer to buffer with clear key data + * @param seckey pointer to buffer receiving the secure key + * @return 0 on success, negative errno value on failure + */ +int pkey_clr2seckey(__u16 cardnr, __u16 domain, __u32 keytype, + const struct pkey_clrkey *clrkey, + struct pkey_seckey *seckey); + +/* + * Derive (AES) proteced key from the (AES) secure key blob. + * @param cardnr may be -1 (use default card) + * @param domain may be -1 (use default domain) + * @param seckey pointer to buffer with the input secure key + * @param protkey pointer to buffer receiving the protected key and + * additional info (type, length) + * @return 0 on success, negative errno value on failure + */ +int pkey_sec2protkey(__u16 cardnr, __u16 domain, + const struct pkey_seckey *seckey, + struct pkey_protkey *protkey); + +/* + * Derive (AES) protected key from a given clear key value. + * @param keytype one of the PKEY_KEYTYPE values + * @param clrkey pointer to buffer with clear key data + * @param protkey pointer to buffer receiving the protected key and + * additional info (type, length) + * @return 0 on success, negative errno value on failure + */ +int pkey_clr2protkey(__u32 keytype, + const struct pkey_clrkey *clrkey, + struct pkey_protkey *protkey); + +/* + * Search for a matching crypto card based on the Master Key + * Verification Pattern provided inside a secure key. + * @param seckey pointer to buffer with the input secure key + * @param cardnr pointer to cardnr, receives the card number on success + * @param domain pointer to domain, receives the domain number on success + * @param verify if set, always verify by fetching verification pattern + * from card + * @return 0 on success, negative errno value on failure. If no card could be + * found, -ENODEV is returned. + */ +int pkey_findcard(const struct pkey_seckey *seckey, + __u16 *cardnr, __u16 *domain, int verify); + +/* + * Find card and transform secure key to protected key. + * @param seckey pointer to buffer with the input secure key + * @param protkey pointer to buffer receiving the protected key and + * additional info (type, length) + * @return 0 on success, negative errno value on failure + */ +int pkey_skey2pkey(const struct pkey_seckey *seckey, + struct pkey_protkey *protkey); + +#endif /* _KAPI_PKEY_H */ diff --git a/arch/s390/include/uapi/asm/Kbuild b/arch/s390/include/uapi/asm/Kbuild index bf736e764cb4..6848ba5c1454 100644 --- a/arch/s390/include/uapi/asm/Kbuild +++ b/arch/s390/include/uapi/asm/Kbuild @@ -24,6 +24,7 @@ header-y += mman.h header-y += monwriter.h header-y += msgbuf.h header-y += param.h +header-y += pkey.h header-y += poll.h header-y += posix_types.h header-y += ptrace.h diff --git a/arch/s390/include/uapi/asm/pkey.h b/arch/s390/include/uapi/asm/pkey.h new file mode 100644 index 000000000000..ed7f19c27ce5 --- /dev/null +++ b/arch/s390/include/uapi/asm/pkey.h @@ -0,0 +1,112 @@ +/* + * Userspace interface to the pkey device driver + * + * Copyright IBM Corp. 2017 + * + * Author: Harald Freudenberger + * + */ + +#ifndef _UAPI_PKEY_H +#define _UAPI_PKEY_H + +#include +#include + +/* + * Ioctl calls supported by the pkey device driver + */ + +#define PKEY_IOCTL_MAGIC 'p' + +#define SECKEYBLOBSIZE 64 /* secure key blob size is always 64 bytes */ +#define MAXPROTKEYSIZE 64 /* a protected key blob may be up to 64 bytes */ +#define MAXCLRKEYSIZE 32 /* a clear key value may be up to 32 bytes */ + +/* defines for the type field within the pkey_protkey struct */ +#define PKEY_KEYTYPE_AES_128 1 +#define PKEY_KEYTYPE_AES_192 2 +#define PKEY_KEYTYPE_AES_256 3 + +/* Struct to hold a secure key blob */ +struct pkey_seckey { + __u8 seckey[SECKEYBLOBSIZE]; /* the secure key blob */ +}; + +/* Struct to hold protected key and length info */ +struct pkey_protkey { + __u32 type; /* key type, one of the PKEY_KEYTYPE values */ + __u32 len; /* bytes actually stored in protkey[] */ + __u8 protkey[MAXPROTKEYSIZE]; /* the protected key blob */ +}; + +/* Struct to hold a clear key value */ +struct pkey_clrkey { + __u8 clrkey[MAXCLRKEYSIZE]; /* 16, 24, or 32 byte clear key value */ +}; + +/* + * Generate secure key + */ +struct pkey_genseck { + __u16 cardnr; /* in: card to use or FFFF for any */ + __u16 domain; /* in: domain or FFFF for any */ + __u32 keytype; /* in: key type to generate */ + struct pkey_seckey seckey; /* out: the secure key blob */ +}; +#define PKEY_GENSECK _IOWR(PKEY_IOCTL_MAGIC, 0x01, struct pkey_genseck) + +/* + * Construct secure key from clear key value + */ +struct pkey_clr2seck { + __u16 cardnr; /* in: card to use or FFFF for any */ + __u16 domain; /* in: domain or FFFF for any */ + __u32 keytype; /* in: key type to generate */ + struct pkey_clrkey clrkey; /* in: the clear key value */ + struct pkey_seckey seckey; /* out: the secure key blob */ +}; +#define PKEY_CLR2SECK _IOWR(PKEY_IOCTL_MAGIC, 0x02, struct pkey_clr2seck) + +/* + * Fabricate protected key from a secure key + */ +struct pkey_sec2protk { + __u16 cardnr; /* in: card to use or FFFF for any */ + __u16 domain; /* in: domain or FFFF for any */ + struct pkey_seckey seckey; /* in: the secure key blob */ + struct pkey_protkey protkey; /* out: the protected key */ +}; +#define PKEY_SEC2PROTK _IOWR(PKEY_IOCTL_MAGIC, 0x03, struct pkey_sec2protk) + +/* + * Fabricate protected key from an clear key value + */ +struct pkey_clr2protk { + __u32 keytype; /* in: key type to generate */ + struct pkey_clrkey clrkey; /* in: the clear key value */ + struct pkey_protkey protkey; /* out: the protected key */ +}; +#define PKEY_CLR2PROTK _IOWR(PKEY_IOCTL_MAGIC, 0x04, struct pkey_clr2protk) + +/* + * Search for matching crypto card based on the Master Key + * Verification Pattern provided inside a secure key. + */ +struct pkey_findcard { + struct pkey_seckey seckey; /* in: the secure key blob */ + __u16 cardnr; /* out: card number */ + __u16 domain; /* out: domain number */ +}; +#define PKEY_FINDCARD _IOWR(PKEY_IOCTL_MAGIC, 0x05, struct pkey_findcard) + +/* + * Combined together: findcard + sec2prot + */ +struct pkey_skey2pkey { + struct pkey_seckey seckey; /* in: the secure key blob */ + struct pkey_protkey protkey; /* out: the protected key */ +}; +#define PKEY_SKEY2PKEY _IOWR(PKEY_IOCTL_MAGIC, 0x06, struct pkey_skey2pkey) + +#endif /* _UAPI_PKEY_H */ diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index ae20ec55ab58..57c2d434ea4b 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -73,6 +73,22 @@ config ZCRYPT + Crypto Express 2,3,4 or 5 Accelerator (CEXxA) + Crypto Express 4 or 5 EP11 Coprocessor (CEXxP) +config PKEY + tristate "Kernel API for protected key handling" + depends on S390 + depends on ZCRYPT + help + With this option enabled the pkey kernel module provides an API + for creation and handling of protected keys. Other parts of the + kernel or userspace applications may use these functions. + + Select this option if you want to enable the kernel and userspace + API for proteced key handling. + + Please note that creation of protected keys from secure keys + requires to have at least one CEX card in coprocessor mode + available at runtime. + config CRYPTO_SHA1_S390 tristate "SHA1 digest algorithm" depends on S390 diff --git a/drivers/s390/crypto/Makefile b/drivers/s390/crypto/Makefile index 0a7fb83f35e5..be36f1010d75 100644 --- a/drivers/s390/crypto/Makefile +++ b/drivers/s390/crypto/Makefile @@ -10,3 +10,7 @@ zcrypt-objs += zcrypt_msgtype6.o zcrypt_msgtype50.o obj-$(CONFIG_ZCRYPT) += zcrypt.o # adapter drivers depend on ap.o and zcrypt.o obj-$(CONFIG_ZCRYPT) += zcrypt_pcixcc.o zcrypt_cex2a.o zcrypt_cex4.o + +# pkey kernel module +pkey-objs := pkey_api.o +obj-$(CONFIG_PKEY) += pkey.o diff --git a/drivers/s390/crypto/pkey_api.c b/drivers/s390/crypto/pkey_api.c new file mode 100644 index 000000000000..40f1136f5568 --- /dev/null +++ b/drivers/s390/crypto/pkey_api.c @@ -0,0 +1,1148 @@ +/* + * pkey device driver + * + * Copyright IBM Corp. 2017 + * Author(s): Harald Freudenberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2 only) + * as published by the Free Software Foundation. + * + */ + +#define KMSG_COMPONENT "pkey" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zcrypt_api.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("IBM Corporation"); +MODULE_DESCRIPTION("s390 protected key interface"); + +/* Size of parameter block used for all cca requests/replies */ +#define PARMBSIZE 512 + +/* Size of vardata block used for some of the cca requests/replies */ +#define VARDATASIZE 4096 + +/* + * debug feature data and functions + */ + +static debug_info_t *debug_info; + +#define DEBUG_DBG(...) debug_sprintf_event(debug_info, 6, ##__VA_ARGS__) +#define DEBUG_INFO(...) debug_sprintf_event(debug_info, 5, ##__VA_ARGS__) +#define DEBUG_WARN(...) debug_sprintf_event(debug_info, 4, ##__VA_ARGS__) +#define DEBUG_ERR(...) debug_sprintf_event(debug_info, 3, ##__VA_ARGS__) + +static void __init pkey_debug_init(void) +{ + debug_info = debug_register("pkey", 1, 1, 4 * sizeof(long)); + debug_register_view(debug_info, &debug_sprintf_view); + debug_set_level(debug_info, 3); +} + +static void __exit pkey_debug_exit(void) +{ + debug_unregister(debug_info); +} + +/* inside view of a secure key token (only type 0x01 version 0x04) */ +struct secaeskeytoken { + u8 type; /* 0x01 for internal key token */ + u8 res0[3]; + u8 version; /* should be 0x04 */ + u8 res1[1]; + u8 flag; /* key flags */ + u8 res2[1]; + u64 mkvp; /* master key verification pattern */ + u8 key[32]; /* key value (encrypted) */ + u8 cv[8]; /* control vector */ + u16 bitsize; /* key bit size */ + u16 keysize; /* key byte size */ + u8 tvv[4]; /* token validation value */ +} __packed; + +/* + * Simple check if the token is a valid CCA secure AES key + * token. If keybitsize is given, the bitsize of the key is + * also checked. Returns 0 on success or errno value on failure. + */ +static int check_secaeskeytoken(u8 *token, int keybitsize) +{ + struct secaeskeytoken *t = (struct secaeskeytoken *) token; + + if (t->type != 0x01) { + DEBUG_ERR( + "check_secaeskeytoken secure token check failed, type mismatch 0x%02x != 0x01\n", + (int) t->type); + return -EINVAL; + } + if (t->version != 0x04) { + DEBUG_ERR( + "check_secaeskeytoken secure token check failed, version mismatch 0x%02x != 0x04\n", + (int) t->version); + return -EINVAL; + } + if (keybitsize > 0 && t->bitsize != keybitsize) { + DEBUG_ERR( + "check_secaeskeytoken secure token check failed, bitsize mismatch %d != %d\n", + (int) t->bitsize, keybitsize); + return -EINVAL; + } + + return 0; +} + +/* + * Allocate consecutive memory for request CPRB, request param + * block, reply CPRB and reply param block and fill in values + * for the common fields. Returns 0 on success or errno value + * on failure. + */ +static int alloc_and_prep_cprbmem(size_t paramblen, + u8 **pcprbmem, + struct CPRBX **preqCPRB, + struct CPRBX **prepCPRB) +{ + u8 *cprbmem; + size_t cprbplusparamblen = sizeof(struct CPRBX) + paramblen; + struct CPRBX *preqcblk, *prepcblk; + + /* + * allocate consecutive memory for request CPRB, request param + * block, reply CPRB and reply param block + */ + cprbmem = kmalloc(2 * cprbplusparamblen, GFP_KERNEL); + if (!cprbmem) + return -ENOMEM; + memset(cprbmem, 0, 2 * cprbplusparamblen); + + preqcblk = (struct CPRBX *) cprbmem; + prepcblk = (struct CPRBX *) (cprbmem + cprbplusparamblen); + + /* fill request cprb struct */ + preqcblk->cprb_len = sizeof(struct CPRBX); + preqcblk->cprb_ver_id = 0x02; + memcpy(preqcblk->func_id, "T2", 2); + preqcblk->rpl_msgbl = cprbplusparamblen; + if (paramblen) { + preqcblk->req_parmb = + ((u8 *) preqcblk) + sizeof(struct CPRBX); + preqcblk->rpl_parmb = + ((u8 *) prepcblk) + sizeof(struct CPRBX); + } + + *pcprbmem = cprbmem; + *preqCPRB = preqcblk; + *prepCPRB = prepcblk; + + return 0; +} + +/* + * Free the cprb memory allocated with the function above. + * If the scrub value is not zero, the memory is filled + * with zeros before freeing (useful if there was some + * clear key material in there). + */ +static void free_cprbmem(void *mem, size_t paramblen, int scrub) +{ + if (scrub) + memzero_explicit(mem, 2 * (sizeof(struct CPRBX) + paramblen)); + kfree(mem); +} + +/* + * Helper function to prepare the xcrb struct + */ +static inline void prep_xcrb(struct ica_xcRB *pxcrb, + u16 cardnr, + struct CPRBX *preqcblk, + struct CPRBX *prepcblk) +{ + memset(pxcrb, 0, sizeof(*pxcrb)); + pxcrb->agent_ID = 0x4341; /* 'CA' */ + pxcrb->user_defined = (cardnr == 0xFFFF ? AUTOSELECT : cardnr); + pxcrb->request_control_blk_length = + preqcblk->cprb_len + preqcblk->req_parml; + pxcrb->request_control_blk_addr = (void *) preqcblk; + pxcrb->reply_control_blk_length = preqcblk->rpl_msgbl; + pxcrb->reply_control_blk_addr = (void *) prepcblk; +} + +/* + * Helper function which calls zcrypt_send_cprb with + * memory management segment adjusted to kernel space + * so that the copy_from_user called within this + * function do in fact copy from kernel space. + */ +static inline int _zcrypt_send_cprb(struct ica_xcRB *xcrb) +{ + int rc; + mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + rc = zcrypt_send_cprb(xcrb); + set_fs(old_fs); + + return rc; +} + +/* + * Generate (random) AES secure key. + */ +int pkey_genseckey(u16 cardnr, u16 domain, + u32 keytype, struct pkey_seckey *seckey) +{ + int i, rc, keysize; + int seckeysize; + u8 *mem; + struct CPRBX *preqcblk, *prepcblk; + struct ica_xcRB xcrb; + struct kgreqparm { + u8 subfunc_code[2]; + u16 rule_array_len; + struct lv1 { + u16 len; + char key_form[8]; + char key_length[8]; + char key_type1[8]; + char key_type2[8]; + } lv1; + struct lv2 { + u16 len; + struct keyid { + u16 len; + u16 attr; + u8 data[SECKEYBLOBSIZE]; + } keyid[6]; + } lv2; + } *preqparm; + struct kgrepparm { + u8 subfunc_code[2]; + u16 rule_array_len; + struct lv3 { + u16 len; + u16 keyblocklen; + struct { + u16 toklen; + u16 tokattr; + u8 tok[0]; + /* ... some more data ... */ + } keyblock; + } lv3; + } *prepparm; + + /* get already prepared memory for 2 cprbs with param block each */ + rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk); + if (rc) + return rc; + + /* fill request cprb struct */ + preqcblk->domain = domain; + + /* fill request cprb param block with KG request */ + preqparm = (struct kgreqparm *) preqcblk->req_parmb; + memcpy(preqparm->subfunc_code, "KG", 2); + preqparm->rule_array_len = sizeof(preqparm->rule_array_len); + preqparm->lv1.len = sizeof(struct lv1); + memcpy(preqparm->lv1.key_form, "OP ", 8); + switch (keytype) { + case PKEY_KEYTYPE_AES_128: + keysize = 16; + memcpy(preqparm->lv1.key_length, "KEYLN16 ", 8); + break; + case PKEY_KEYTYPE_AES_192: + keysize = 24; + memcpy(preqparm->lv1.key_length, "KEYLN24 ", 8); + break; + case PKEY_KEYTYPE_AES_256: + keysize = 32; + memcpy(preqparm->lv1.key_length, "KEYLN32 ", 8); + break; + default: + DEBUG_ERR( + "pkey_genseckey unknown/unsupported keytype %d\n", + keytype); + rc = -EINVAL; + goto out; + } + memcpy(preqparm->lv1.key_type1, "AESDATA ", 8); + preqparm->lv2.len = sizeof(struct lv2); + for (i = 0; i < 6; i++) { + preqparm->lv2.keyid[i].len = sizeof(struct keyid); + preqparm->lv2.keyid[i].attr = (i == 2 ? 0x30 : 0x10); + } + preqcblk->req_parml = sizeof(struct kgreqparm); + + /* fill xcrb struct */ + prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk); + + /* forward xcrb with request CPRB and reply CPRB to zcrypt dd */ + rc = _zcrypt_send_cprb(&xcrb); + if (rc) { + DEBUG_ERR( + "pkey_genseckey zcrypt_send_cprb (cardnr=%d domain=%d) failed with errno %d\n", + (int) cardnr, (int) domain, rc); + goto out; + } + + /* check response returncode and reasoncode */ + if (prepcblk->ccp_rtcode != 0) { + DEBUG_ERR( + "pkey_genseckey secure key generate failure, card response %d/%d\n", + (int) prepcblk->ccp_rtcode, + (int) prepcblk->ccp_rscode); + rc = -EIO; + goto out; + } + + /* process response cprb param block */ + prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX); + prepparm = (struct kgrepparm *) prepcblk->rpl_parmb; + + /* check length of the returned secure key token */ + seckeysize = prepparm->lv3.keyblock.toklen + - sizeof(prepparm->lv3.keyblock.toklen) + - sizeof(prepparm->lv3.keyblock.tokattr); + if (seckeysize != SECKEYBLOBSIZE) { + DEBUG_ERR( + "pkey_genseckey secure token size mismatch %d != %d bytes\n", + seckeysize, SECKEYBLOBSIZE); + rc = -EIO; + goto out; + } + + /* check secure key token */ + rc = check_secaeskeytoken(prepparm->lv3.keyblock.tok, 8*keysize); + if (rc) { + rc = -EIO; + goto out; + } + + /* copy the generated secure key token */ + memcpy(seckey->seckey, prepparm->lv3.keyblock.tok, SECKEYBLOBSIZE); + +out: + free_cprbmem(mem, PARMBSIZE, 0); + return rc; +} +EXPORT_SYMBOL(pkey_genseckey); + +/* + * Generate an AES secure key with given key value. + */ +int pkey_clr2seckey(u16 cardnr, u16 domain, u32 keytype, + const struct pkey_clrkey *clrkey, + struct pkey_seckey *seckey) +{ + int rc, keysize, seckeysize; + u8 *mem; + struct CPRBX *preqcblk, *prepcblk; + struct ica_xcRB xcrb; + struct cmreqparm { + u8 subfunc_code[2]; + u16 rule_array_len; + char rule_array[8]; + struct lv1 { + u16 len; + u8 clrkey[0]; + } lv1; + struct lv2 { + u16 len; + struct keyid { + u16 len; + u16 attr; + u8 data[SECKEYBLOBSIZE]; + } keyid; + } lv2; + } *preqparm; + struct lv2 *plv2; + struct cmrepparm { + u8 subfunc_code[2]; + u16 rule_array_len; + struct lv3 { + u16 len; + u16 keyblocklen; + struct { + u16 toklen; + u16 tokattr; + u8 tok[0]; + /* ... some more data ... */ + } keyblock; + } lv3; + } *prepparm; + + /* get already prepared memory for 2 cprbs with param block each */ + rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk); + if (rc) + return rc; + + /* fill request cprb struct */ + preqcblk->domain = domain; + + /* fill request cprb param block with CM request */ + preqparm = (struct cmreqparm *) preqcblk->req_parmb; + memcpy(preqparm->subfunc_code, "CM", 2); + memcpy(preqparm->rule_array, "AES ", 8); + preqparm->rule_array_len = + sizeof(preqparm->rule_array_len) + sizeof(preqparm->rule_array); + switch (keytype) { + case PKEY_KEYTYPE_AES_128: + keysize = 16; + break; + case PKEY_KEYTYPE_AES_192: + keysize = 24; + break; + case PKEY_KEYTYPE_AES_256: + keysize = 32; + break; + default: + DEBUG_ERR( + "pkey_clr2seckey unknown/unsupported keytype %d\n", + keytype); + rc = -EINVAL; + goto out; + } + preqparm->lv1.len = sizeof(struct lv1) + keysize; + memcpy(preqparm->lv1.clrkey, clrkey->clrkey, keysize); + plv2 = (struct lv2 *) (((u8 *) &preqparm->lv2) + keysize); + plv2->len = sizeof(struct lv2); + plv2->keyid.len = sizeof(struct keyid); + plv2->keyid.attr = 0x30; + preqcblk->req_parml = sizeof(struct cmreqparm) + keysize; + + /* fill xcrb struct */ + prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk); + + /* forward xcrb with request CPRB and reply CPRB to zcrypt dd */ + rc = _zcrypt_send_cprb(&xcrb); + if (rc) { + DEBUG_ERR( + "pkey_clr2seckey zcrypt_send_cprb (cardnr=%d domain=%d) failed with errno %d\n", + (int) cardnr, (int) domain, rc); + goto out; + } + + /* check response returncode and reasoncode */ + if (prepcblk->ccp_rtcode != 0) { + DEBUG_ERR( + "pkey_clr2seckey clear key import failure, card response %d/%d\n", + (int) prepcblk->ccp_rtcode, + (int) prepcblk->ccp_rscode); + rc = -EIO; + goto out; + } + + /* process response cprb param block */ + prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX); + prepparm = (struct cmrepparm *) prepcblk->rpl_parmb; + + /* check length of the returned secure key token */ + seckeysize = prepparm->lv3.keyblock.toklen + - sizeof(prepparm->lv3.keyblock.toklen) + - sizeof(prepparm->lv3.keyblock.tokattr); + if (seckeysize != SECKEYBLOBSIZE) { + DEBUG_ERR( + "pkey_clr2seckey secure token size mismatch %d != %d bytes\n", + seckeysize, SECKEYBLOBSIZE); + rc = -EIO; + goto out; + } + + /* check secure key token */ + rc = check_secaeskeytoken(prepparm->lv3.keyblock.tok, 8*keysize); + if (rc) { + rc = -EIO; + goto out; + } + + /* copy the generated secure key token */ + memcpy(seckey->seckey, prepparm->lv3.keyblock.tok, SECKEYBLOBSIZE); + +out: + free_cprbmem(mem, PARMBSIZE, 1); + return rc; +} +EXPORT_SYMBOL(pkey_clr2seckey); + +/* + * Derive a proteced key from the secure key blob. + */ +int pkey_sec2protkey(u16 cardnr, u16 domain, + const struct pkey_seckey *seckey, + struct pkey_protkey *protkey) +{ + int rc; + u8 *mem; + struct CPRBX *preqcblk, *prepcblk; + struct ica_xcRB xcrb; + struct uskreqparm { + u8 subfunc_code[2]; + u16 rule_array_len; + struct lv1 { + u16 len; + u16 attr_len; + u16 attr_flags; + } lv1; + struct lv2 { + u16 len; + u16 attr_len; + u16 attr_flags; + u8 token[0]; /* cca secure key token */ + } lv2 __packed; + } *preqparm; + struct uskrepparm { + u8 subfunc_code[2]; + u16 rule_array_len; + struct lv3 { + u16 len; + u16 attr_len; + u16 attr_flags; + struct cpacfkeyblock { + u8 version; /* version of this struct */ + u8 flags[2]; + u8 algo; + u8 form; + u8 pad1[3]; + u16 keylen; + u8 key[64]; /* the key (keylen bytes) */ + u16 keyattrlen; + u8 keyattr[32]; + u8 pad2[1]; + u8 vptype; + u8 vp[32]; /* verification pattern */ + } keyblock; + } lv3 __packed; + } *prepparm; + + /* get already prepared memory for 2 cprbs with param block each */ + rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk); + if (rc) + return rc; + + /* fill request cprb struct */ + preqcblk->domain = domain; + + /* fill request cprb param block with USK request */ + preqparm = (struct uskreqparm *) preqcblk->req_parmb; + memcpy(preqparm->subfunc_code, "US", 2); + preqparm->rule_array_len = sizeof(preqparm->rule_array_len); + preqparm->lv1.len = sizeof(struct lv1); + preqparm->lv1.attr_len = sizeof(struct lv1) - sizeof(preqparm->lv1.len); + preqparm->lv1.attr_flags = 0x0001; + preqparm->lv2.len = sizeof(struct lv2) + SECKEYBLOBSIZE; + preqparm->lv2.attr_len = sizeof(struct lv2) + - sizeof(preqparm->lv2.len) + SECKEYBLOBSIZE; + preqparm->lv2.attr_flags = 0x0000; + memcpy(preqparm->lv2.token, seckey->seckey, SECKEYBLOBSIZE); + preqcblk->req_parml = sizeof(struct uskreqparm) + SECKEYBLOBSIZE; + + /* fill xcrb struct */ + prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk); + + /* forward xcrb with request CPRB and reply CPRB to zcrypt dd */ + rc = _zcrypt_send_cprb(&xcrb); + if (rc) { + DEBUG_ERR( + "pkey_sec2protkey zcrypt_send_cprb (cardnr=%d domain=%d) failed with errno %d\n", + (int) cardnr, (int) domain, rc); + goto out; + } + + /* check response returncode and reasoncode */ + if (prepcblk->ccp_rtcode != 0) { + DEBUG_ERR( + "pkey_sec2protkey unwrap secure key failure, card response %d/%d\n", + (int) prepcblk->ccp_rtcode, + (int) prepcblk->ccp_rscode); + rc = -EIO; + goto out; + } + + /* process response cprb param block */ + prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX); + prepparm = (struct uskrepparm *) prepcblk->rpl_parmb; + + /* check the returned keyblock */ + if (prepparm->lv3.keyblock.version != 0x01) { + DEBUG_ERR( + "pkey_sec2protkey reply param keyblock version mismatch 0x%02x != 0x01\n", + (int) prepparm->lv3.keyblock.version); + rc = -EIO; + goto out; + } + + /* copy the tanslated protected key */ + switch (prepparm->lv3.keyblock.keylen) { + case 16+32: + protkey->type = PKEY_KEYTYPE_AES_128; + break; + case 24+32: + protkey->type = PKEY_KEYTYPE_AES_192; + break; + case 32+32: + protkey->type = PKEY_KEYTYPE_AES_256; + break; + default: + DEBUG_ERR("pkey_sec2protkey unknown/unsupported keytype %d\n", + prepparm->lv3.keyblock.keylen); + rc = -EIO; + goto out; + } + protkey->len = prepparm->lv3.keyblock.keylen; + memcpy(protkey->protkey, prepparm->lv3.keyblock.key, protkey->len); + +out: + free_cprbmem(mem, PARMBSIZE, 0); + return rc; +} +EXPORT_SYMBOL(pkey_sec2protkey); + +/* + * Create a protected key from a clear key value. + */ +int pkey_clr2protkey(u32 keytype, + const struct pkey_clrkey *clrkey, + struct pkey_protkey *protkey) +{ + long fc; + int keysize; + u8 paramblock[64]; + + switch (keytype) { + case PKEY_KEYTYPE_AES_128: + keysize = 16; + fc = CPACF_PCKMO_ENC_AES_128_KEY; + break; + case PKEY_KEYTYPE_AES_192: + keysize = 24; + fc = CPACF_PCKMO_ENC_AES_192_KEY; + break; + case PKEY_KEYTYPE_AES_256: + keysize = 32; + fc = CPACF_PCKMO_ENC_AES_256_KEY; + break; + default: + DEBUG_ERR("pkey_clr2protkey unknown/unsupported keytype %d\n", + keytype); + return -EINVAL; + } + + /* prepare param block */ + memset(paramblock, 0, sizeof(paramblock)); + memcpy(paramblock, clrkey->clrkey, keysize); + + /* call the pckmo instruction */ + cpacf_pckmo(fc, paramblock); + + /* copy created protected key */ + protkey->type = keytype; + protkey->len = keysize + 32; + memcpy(protkey->protkey, paramblock, keysize + 32); + + return 0; +} +EXPORT_SYMBOL(pkey_clr2protkey); + +/* + * query cryptographic facility from adapter + */ +static int query_crypto_facility(u16 cardnr, u16 domain, + const char *keyword, + u8 *rarray, size_t *rarraylen, + u8 *varray, size_t *varraylen) +{ + int rc; + u16 len; + u8 *mem, *ptr; + struct CPRBX *preqcblk, *prepcblk; + struct ica_xcRB xcrb; + struct fqreqparm { + u8 subfunc_code[2]; + u16 rule_array_len; + char rule_array[8]; + struct lv1 { + u16 len; + u8 data[VARDATASIZE]; + } lv1; + u16 dummylen; + } *preqparm; + size_t parmbsize = sizeof(struct fqreqparm); + struct fqrepparm { + u8 subfunc_code[2]; + u8 lvdata[0]; + } *prepparm; + + /* get already prepared memory for 2 cprbs with param block each */ + rc = alloc_and_prep_cprbmem(parmbsize, &mem, &preqcblk, &prepcblk); + if (rc) + return rc; + + /* fill request cprb struct */ + preqcblk->domain = domain; + + /* fill request cprb param block with FQ request */ + preqparm = (struct fqreqparm *) preqcblk->req_parmb; + memcpy(preqparm->subfunc_code, "FQ", 2); + strncpy(preqparm->rule_array, keyword, sizeof(preqparm->rule_array)); + preqparm->rule_array_len = + sizeof(preqparm->rule_array_len) + sizeof(preqparm->rule_array); + preqparm->lv1.len = sizeof(preqparm->lv1); + preqparm->dummylen = sizeof(preqparm->dummylen); + preqcblk->req_parml = parmbsize; + + /* fill xcrb struct */ + prep_xcrb(&xcrb, cardnr, preqcblk, prepcblk); + + /* forward xcrb with request CPRB and reply CPRB to zcrypt dd */ + rc = _zcrypt_send_cprb(&xcrb); + if (rc) { + DEBUG_ERR( + "query_crypto_facility zcrypt_send_cprb (cardnr=%d domain=%d) failed with errno %d\n", + (int) cardnr, (int) domain, rc); + goto out; + } + + /* check response returncode and reasoncode */ + if (prepcblk->ccp_rtcode != 0) { + DEBUG_ERR( + "query_crypto_facility unwrap secure key failure, card response %d/%d\n", + (int) prepcblk->ccp_rtcode, + (int) prepcblk->ccp_rscode); + rc = -EIO; + goto out; + } + + /* process response cprb param block */ + prepcblk->rpl_parmb = ((u8 *) prepcblk) + sizeof(struct CPRBX); + prepparm = (struct fqrepparm *) prepcblk->rpl_parmb; + ptr = prepparm->lvdata; + + /* check and possibly copy reply rule array */ + len = *((u16 *) ptr); + if (len > sizeof(u16)) { + ptr += sizeof(u16); + len -= sizeof(u16); + if (rarray && rarraylen && *rarraylen > 0) { + *rarraylen = (len > *rarraylen ? *rarraylen : len); + memcpy(rarray, ptr, *rarraylen); + } + ptr += len; + } + /* check and possible copy reply var array */ + len = *((u16 *) ptr); + if (len > sizeof(u16)) { + ptr += sizeof(u16); + len -= sizeof(u16); + if (varray && varraylen && *varraylen > 0) { + *varraylen = (len > *varraylen ? *varraylen : len); + memcpy(varray, ptr, *varraylen); + } + ptr += len; + } + +out: + free_cprbmem(mem, parmbsize, 0); + return rc; +} + +/* + * Fetch just the mkvp value via query_crypto_facility from adapter. + */ +static int fetch_mkvp(u16 cardnr, u16 domain, u64 *mkvp) +{ + int rc, found = 0; + size_t rlen, vlen; + u8 *rarray, *varray, *pg; + + pg = (u8 *) __get_free_page(GFP_KERNEL); + if (!pg) + return -ENOMEM; + rarray = pg; + varray = pg + PAGE_SIZE/2; + rlen = vlen = PAGE_SIZE/2; + + rc = query_crypto_facility(cardnr, domain, "STATICSA", + rarray, &rlen, varray, &vlen); + if (rc == 0 && rlen > 8*8 && vlen > 184+8) { + if (rarray[64] == '2') { + /* current master key state is valid */ + *mkvp = *((u64 *)(varray + 184)); + found = 1; + } + } + + free_page((unsigned long) pg); + + return found ? 0 : -ENOENT; +} + +/* struct to hold cached mkvp info for each card/domain */ +struct mkvp_info { + struct list_head list; + u16 cardnr; + u16 domain; + u64 mkvp; +}; + +/* a list with mkvp_info entries */ +static LIST_HEAD(mkvp_list); +static DEFINE_SPINLOCK(mkvp_list_lock); + +static int mkvp_cache_fetch(u16 cardnr, u16 domain, u64 *mkvp) +{ + int rc = -ENOENT; + struct mkvp_info *ptr; + + spin_lock_bh(&mkvp_list_lock); + list_for_each_entry(ptr, &mkvp_list, list) { + if (ptr->cardnr == cardnr && + ptr->domain == domain) { + *mkvp = ptr->mkvp; + rc = 0; + break; + } + } + spin_unlock_bh(&mkvp_list_lock); + + return rc; +} + +static void mkvp_cache_update(u16 cardnr, u16 domain, u64 mkvp) +{ + int found = 0; + struct mkvp_info *ptr; + + spin_lock_bh(&mkvp_list_lock); + list_for_each_entry(ptr, &mkvp_list, list) { + if (ptr->cardnr == cardnr && + ptr->domain == domain) { + ptr->mkvp = mkvp; + found = 1; + break; + } + } + if (!found) { + ptr = kmalloc(sizeof(*ptr), GFP_ATOMIC); + if (!ptr) { + spin_unlock_bh(&mkvp_list_lock); + return; + } + ptr->cardnr = cardnr; + ptr->domain = domain; + ptr->mkvp = mkvp; + list_add(&ptr->list, &mkvp_list); + } + spin_unlock_bh(&mkvp_list_lock); +} + +static void mkvp_cache_scrub(u16 cardnr, u16 domain) +{ + struct mkvp_info *ptr; + + spin_lock_bh(&mkvp_list_lock); + list_for_each_entry(ptr, &mkvp_list, list) { + if (ptr->cardnr == cardnr && + ptr->domain == domain) { + list_del(&ptr->list); + kfree(ptr); + break; + } + } + spin_unlock_bh(&mkvp_list_lock); +} + +static void __exit mkvp_cache_free(void) +{ + struct mkvp_info *ptr, *pnext; + + spin_lock_bh(&mkvp_list_lock); + list_for_each_entry_safe(ptr, pnext, &mkvp_list, list) { + list_del(&ptr->list); + kfree(ptr); + } + spin_unlock_bh(&mkvp_list_lock); +} + +/* + * Search for a matching crypto card based on the Master Key + * Verification Pattern provided inside a secure key. + */ +int pkey_findcard(const struct pkey_seckey *seckey, + u16 *pcardnr, u16 *pdomain, int verify) +{ + struct secaeskeytoken *t = (struct secaeskeytoken *) seckey; + struct zcrypt_device_matrix *device_matrix; + u16 card, dom; + u64 mkvp; + int i, rc; + + /* mkvp must not be zero */ + if (t->mkvp == 0) + return -EINVAL; + + /* fetch status of all crypto cards */ + device_matrix = kmalloc(sizeof(struct zcrypt_device_matrix), + GFP_KERNEL); + if (!device_matrix) + return -ENOMEM; + zcrypt_device_status_mask(device_matrix); + + /* walk through all crypto cards */ + for (i = 0; i < MAX_ZDEV_ENTRIES; i++) { + card = AP_QID_CARD(device_matrix->device[i].qid); + dom = AP_QID_QUEUE(device_matrix->device[i].qid); + if (device_matrix->device[i].online && + device_matrix->device[i].functions & 0x04) { + /* an enabled CCA Coprocessor card */ + /* try cached mkvp */ + if (mkvp_cache_fetch(card, dom, &mkvp) == 0 && + t->mkvp == mkvp) { + if (!verify) + break; + /* verify: fetch mkvp from adapter */ + if (fetch_mkvp(card, dom, &mkvp) == 0) { + mkvp_cache_update(card, dom, mkvp); + if (t->mkvp == mkvp) + break; + } + } + } else { + /* Card is offline and/or not a CCA card. */ + /* del mkvp entry from cache if it exists */ + mkvp_cache_scrub(card, dom); + } + } + if (i >= MAX_ZDEV_ENTRIES) { + /* nothing found, so this time without cache */ + for (i = 0; i < MAX_ZDEV_ENTRIES; i++) { + if (!(device_matrix->device[i].online && + device_matrix->device[i].functions & 0x04)) + continue; + card = AP_QID_CARD(device_matrix->device[i].qid); + dom = AP_QID_QUEUE(device_matrix->device[i].qid); + /* fresh fetch mkvp from adapter */ + if (fetch_mkvp(card, dom, &mkvp) == 0) { + mkvp_cache_update(card, dom, mkvp); + if (t->mkvp == mkvp) + break; + } + } + } + if (i < MAX_ZDEV_ENTRIES) { + if (pcardnr) + *pcardnr = card; + if (pdomain) + *pdomain = dom; + rc = 0; + } else + rc = -ENODEV; + + kfree(device_matrix); + return rc; +} +EXPORT_SYMBOL(pkey_findcard); + +/* + * Find card and transform secure key into protected key. + */ +int pkey_skey2pkey(const struct pkey_seckey *seckey, + struct pkey_protkey *protkey) +{ + u16 cardnr, domain; + int rc, verify; + + /* + * The pkey_sec2protkey call may fail when a card has been + * addressed where the master key was changed after last fetch + * of the mkvp into the cache. So first try without verify then + * with verify enabled (thus refreshing the mkvp for each card). + */ + for (verify = 0; verify < 2; verify++) { + rc = pkey_findcard(seckey, &cardnr, &domain, verify); + if (rc) + continue; + rc = pkey_sec2protkey(cardnr, domain, seckey, protkey); + if (rc == 0) + break; + } + + if (rc) + DEBUG_DBG("pkey_skey2pkey failed rc=%d\n", rc); + + return rc; +} +EXPORT_SYMBOL(pkey_skey2pkey); + +/* + * File io functions + */ + +static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int rc; + + switch (cmd) { + case PKEY_GENSECK: { + struct pkey_genseck __user *ugs = (void __user *) arg; + struct pkey_genseck kgs; + + if (copy_from_user(&kgs, ugs, sizeof(kgs))) + return -EFAULT; + rc = pkey_genseckey(kgs.cardnr, kgs.domain, + kgs.keytype, &kgs.seckey); + DEBUG_DBG("pkey_ioctl pkey_genseckey()=%d\n", rc); + if (rc) + break; + if (copy_to_user(ugs, &kgs, sizeof(kgs))) + return -EFAULT; + break; + } + case PKEY_CLR2SECK: { + struct pkey_clr2seck __user *ucs = (void __user *) arg; + struct pkey_clr2seck kcs; + + if (copy_from_user(&kcs, ucs, sizeof(kcs))) + return -EFAULT; + rc = pkey_clr2seckey(kcs.cardnr, kcs.domain, kcs.keytype, + &kcs.clrkey, &kcs.seckey); + DEBUG_DBG("pkey_ioctl pkey_clr2seckey()=%d\n", rc); + if (rc) + break; + if (copy_to_user(ucs, &kcs, sizeof(kcs))) + return -EFAULT; + memzero_explicit(&kcs, sizeof(kcs)); + break; + } + case PKEY_SEC2PROTK: { + struct pkey_sec2protk __user *usp = (void __user *) arg; + struct pkey_sec2protk ksp; + + if (copy_from_user(&ksp, usp, sizeof(ksp))) + return -EFAULT; + rc = pkey_sec2protkey(ksp.cardnr, ksp.domain, + &ksp.seckey, &ksp.protkey); + DEBUG_DBG("pkey_ioctl pkey_sec2protkey()=%d\n", rc); + if (rc) + break; + if (copy_to_user(usp, &ksp, sizeof(ksp))) + return -EFAULT; + break; + } + case PKEY_CLR2PROTK: { + struct pkey_clr2protk __user *ucp = (void __user *) arg; + struct pkey_clr2protk kcp; + + if (copy_from_user(&kcp, ucp, sizeof(kcp))) + return -EFAULT; + rc = pkey_clr2protkey(kcp.keytype, + &kcp.clrkey, &kcp.protkey); + DEBUG_DBG("pkey_ioctl pkey_clr2protkey()=%d\n", rc); + if (rc) + break; + if (copy_to_user(ucp, &kcp, sizeof(kcp))) + return -EFAULT; + memzero_explicit(&kcp, sizeof(kcp)); + break; + } + case PKEY_FINDCARD: { + struct pkey_findcard __user *ufc = (void __user *) arg; + struct pkey_findcard kfc; + + if (copy_from_user(&kfc, ufc, sizeof(kfc))) + return -EFAULT; + rc = pkey_findcard(&kfc.seckey, + &kfc.cardnr, &kfc.domain, 1); + DEBUG_DBG("pkey_ioctl pkey_findcard()=%d\n", rc); + if (rc) + break; + if (copy_to_user(ufc, &kfc, sizeof(kfc))) + return -EFAULT; + break; + } + case PKEY_SKEY2PKEY: { + struct pkey_skey2pkey __user *usp = (void __user *) arg; + struct pkey_skey2pkey ksp; + + if (copy_from_user(&ksp, usp, sizeof(ksp))) + return -EFAULT; + rc = pkey_skey2pkey(&ksp.seckey, &ksp.protkey); + DEBUG_DBG("pkey_ioctl pkey_skey2pkey()=%d\n", rc); + if (rc) + break; + if (copy_to_user(usp, &ksp, sizeof(ksp))) + return -EFAULT; + break; + } + default: + /* unknown/unsupported ioctl cmd */ + return -ENOTTY; + } + + return rc; +} + +/* + * Sysfs and file io operations + */ +static const struct file_operations pkey_fops = { + .owner = THIS_MODULE, + .open = nonseekable_open, + .llseek = no_llseek, + .unlocked_ioctl = pkey_unlocked_ioctl, +}; + +static struct miscdevice pkey_dev = { + .name = "pkey", + .minor = MISC_DYNAMIC_MINOR, + .mode = 0666, + .fops = &pkey_fops, +}; + +/* + * Module init + */ +int __init pkey_init(void) +{ + cpacf_mask_t pckmo_functions; + + /* check for pckmo instructions available */ + if (!cpacf_query(CPACF_PCKMO, &pckmo_functions)) + return -EOPNOTSUPP; + if (!cpacf_test_func(&pckmo_functions, CPACF_PCKMO_ENC_AES_128_KEY) || + !cpacf_test_func(&pckmo_functions, CPACF_PCKMO_ENC_AES_192_KEY) || + !cpacf_test_func(&pckmo_functions, CPACF_PCKMO_ENC_AES_256_KEY)) + return -EOPNOTSUPP; + + pkey_debug_init(); + + return misc_register(&pkey_dev); +} + +/* + * Module exit + */ +static void __exit pkey_exit(void) +{ + misc_deregister(&pkey_dev); + mkvp_cache_free(); + pkey_debug_exit(); +} + +module_init(pkey_init); +module_exit(pkey_exit); -- cgit v1.2.3 From 8693b9145b13dc44664602cd4cbe71862c26d0b5 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 23 Feb 2017 11:06:09 +0000 Subject: s390/dasd: fix spelling mistake: "supportet" -> "supported" trivial fix to spelling mistake in literal string Signed-off-by: Colin Ian King 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 0f1713727d4c..0b38217f8147 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -4864,7 +4864,7 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, break; case 3: /* tsa_intrg */ len += sprintf(page + len, PRINTK_HEADER - " tsb->tsa.intrg.: not supportet yet\n"); + " tsb->tsa.intrg.: not supported yet\n"); break; } -- cgit v1.2.3