From 978857c7e367d6841f71c4ded5a8c244520f5e22 Mon Sep 17 00:00:00 2001 From: Benjamin Block Date: Fri, 8 May 2020 19:23:28 +0200 Subject: scsi: zfcp: Move shost modification after QDIO (re-)open into fenced function When establishing and activating the QDIO queue pair for a FCP device for the first time, or after an adapter recovery, we publish some of its characteristics to the scsi host object representing that FCP device. When moving the scsi host object allocation and registration to after the first exchange config and exchange port data, this is not possible for the former case - QDIO open for the first time - because that happens before exchange config and exchange port data. Move the scsi host object update into a fenced function that checks whether the object already exists or not. This way we can repeat that step later, once we are past the allocation. Once the first recovery succeeds we don't release the scsi host object anymore, so further recoveries do work as before. Link: https://lore.kernel.org/r/a214ebf508f71e3690113e3e90edab1cea0e24e3.1588956679.git.bblock@linux.ibm.com Reviewed-by: Steffen Maier Signed-off-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_ext.h | 2 ++ drivers/s390/scsi/zfcp_qdio.c | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 88294ca0e2ea..d67f31b9b859 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -153,6 +153,8 @@ extern int zfcp_qdio_sbal_get(struct zfcp_qdio *); extern int zfcp_qdio_send(struct zfcp_qdio *, struct zfcp_qdio_req *); extern int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *, struct zfcp_qdio_req *, struct scatterlist *); +extern void zfcp_qdio_shost_update(struct zfcp_adapter *const adapter, + const struct zfcp_qdio *const qdio); extern int zfcp_qdio_open(struct zfcp_qdio *); extern void zfcp_qdio_close(struct zfcp_qdio *); extern void zfcp_qdio_siosl(struct zfcp_adapter *); diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 26702b56a7ab..3a7f3374d10a 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -4,7 +4,7 @@ * * Setup and helper functions to access QDIO. * - * Copyright IBM Corp. 2002, 2017 + * Copyright IBM Corp. 2002, 2020 */ #define KMSG_COMPONENT "zfcp" @@ -342,6 +342,18 @@ void zfcp_qdio_close(struct zfcp_qdio *qdio) atomic_set(&qdio->req_q_free, 0); } +void zfcp_qdio_shost_update(struct zfcp_adapter *const adapter, + const struct zfcp_qdio *const qdio) +{ + struct Scsi_Host *const shost = adapter->scsi_host; + + if (shost == NULL) + return; + + shost->sg_tablesize = qdio->max_sbale_per_req; + shost->max_sectors = qdio->max_sbale_per_req * 8; +} + /** * zfcp_qdio_open - prepare and initialize response queue * @qdio: pointer to struct zfcp_qdio @@ -420,10 +432,7 @@ int zfcp_qdio_open(struct zfcp_qdio *qdio) atomic_set(&qdio->req_q_free, QDIO_MAX_BUFFERS_PER_Q); atomic_or(ZFCP_STATUS_ADAPTER_QDIOUP, &qdio->adapter->status); - if (adapter->scsi_host) { - adapter->scsi_host->sg_tablesize = qdio->max_sbale_per_req; - adapter->scsi_host->max_sectors = qdio->max_sbale_per_req * 8; - } + zfcp_qdio_shost_update(adapter, qdio); return 0; -- cgit v1.2.3-59-g8ed1b From bd1684817d7d8d1a3b95a4347166246ad1f7670b Mon Sep 17 00:00:00 2001 From: Benjamin Block Date: Fri, 8 May 2020 19:23:29 +0200 Subject: scsi: zfcp: Move shost updates during xconfig data handling into fenced function When executing exchange config data for a FCP device for the first time, or after an adapter recovery, we update several properties of the scsi host or fibre channel host object that represent that FCP device. When moving the scsi host object allocation and registration - and thus also the fibre channel host object allocation - to after the first exchange config and exchange port data, this is not possible for the former case. Move all these update into separate, and fenced function that first checks whether the scsi host object already exists or not, before making the updates. During the first ever exchange config data in the adapter life cycle this will make the exchange config data handler skip over this update step, but we can repeat it later, after we allocated the scsi host object. For any further recovery of that adapter the work flow is only changed slightly because then the scsi host object already exists and we don't free it until we release the adapter completely at the end of its life cycle. Link: https://lore.kernel.org/r/5fc3f4d38d4334f7aa595497c6f7865fb1102e0f.1588956679.git.bblock@linux.ibm.com Reviewed-by: Steffen Maier Signed-off-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_ext.h | 5 +++ drivers/s390/scsi/zfcp_fsf.c | 48 ++++------------------------- drivers/s390/scsi/zfcp_scsi.c | 72 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 43 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index d67f31b9b859..4c4be754cf6e 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -125,6 +125,7 @@ extern int zfcp_fsf_exchange_config_data_sync(struct zfcp_qdio *, extern int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *); extern int zfcp_fsf_exchange_port_data_sync(struct zfcp_qdio *, struct fsf_qtcb_bottom_port *); +extern u32 zfcp_fsf_convert_portspeed(u32 fsf_speed); extern void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *); extern int zfcp_fsf_status_read(struct zfcp_qdio *); extern int zfcp_status_read_refill(struct zfcp_adapter *adapter); @@ -171,6 +172,10 @@ extern void zfcp_scsi_schedule_rport_block(struct zfcp_port *); extern void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *); extern void zfcp_scsi_set_prot(struct zfcp_adapter *); extern void zfcp_scsi_dif_sense_error(struct scsi_cmnd *, int); +extern void zfcp_scsi_shost_update_config_data( + struct zfcp_adapter *const adapter, + const struct fsf_qtcb_bottom_config *const bottom, + const bool bottom_incomplete); /* zfcp_sysfs.c */ extern const struct attribute_group *zfcp_unit_attr_groups[]; diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 111fe3fc32d7..54edfcbe84ce 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -479,7 +479,7 @@ void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter) #define ZFCP_FSF_PORTSPEED_128GBIT (1 << 8) #define ZFCP_FSF_PORTSPEED_NOT_NEGOTIATED (1 << 15) -static u32 zfcp_fsf_convert_portspeed(u32 fsf_speed) +u32 zfcp_fsf_convert_portspeed(u32 fsf_speed) { u32 fdmi_speed = 0; if (fsf_speed & ZFCP_FSF_PORTSPEED_1GBIT) @@ -509,64 +509,36 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req) { struct fsf_qtcb_bottom_config *bottom = &req->qtcb->bottom.config; struct zfcp_adapter *adapter = req->adapter; - struct Scsi_Host *shost = adapter->scsi_host; - struct fc_els_flogi *nsp, *plogi; + struct fc_els_flogi *plogi; /* adjust pointers for missing command code */ - nsp = (struct fc_els_flogi *) ((u8 *)&bottom->nport_serv_param - - sizeof(u32)); plogi = (struct fc_els_flogi *) ((u8 *)&bottom->plogi_payload - sizeof(u32)); if (req->data) memcpy(req->data, bottom, sizeof(*bottom)); - snprintf(fc_host_manufacturer(shost), FC_SERIAL_NUMBER_SIZE, "%s", - "IBM"); - fc_host_port_name(shost) = be64_to_cpu(nsp->fl_wwpn); - fc_host_node_name(shost) = be64_to_cpu(nsp->fl_wwnn); - fc_host_supported_classes(shost) = FC_COS_CLASS2 | FC_COS_CLASS3; - adapter->timer_ticks = bottom->timer_interval & ZFCP_FSF_TIMER_INT_MASK; adapter->stat_read_buf_num = max(bottom->status_read_buf_num, (u16)FSF_STATUS_READS_RECOM); - zfcp_scsi_set_prot(adapter); - /* no error return above here, otherwise must fix call chains */ /* do not evaluate invalid fields */ if (req->qtcb->header.fsf_status == FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE) return 0; - fc_host_port_id(shost) = ntoh24(bottom->s_id); - fc_host_speed(shost) = - zfcp_fsf_convert_portspeed(bottom->fc_link_speed); - adapter->hydra_version = bottom->adapter_type; - snprintf(fc_host_model(shost), FC_SYMBOLIC_NAME_SIZE, "0x%04x", - bottom->adapter_type); switch (bottom->fc_topology) { case FSF_TOPO_P2P: adapter->peer_d_id = ntoh24(bottom->peer_d_id); adapter->peer_wwpn = be64_to_cpu(plogi->fl_wwpn); adapter->peer_wwnn = be64_to_cpu(plogi->fl_wwnn); - fc_host_port_type(shost) = FC_PORTTYPE_PTP; - fc_host_fabric_name(shost) = 0; break; case FSF_TOPO_FABRIC: - fc_host_fabric_name(shost) = be64_to_cpu(plogi->fl_wwnn); - if (bottom->connection_features & FSF_FEATURE_NPIV_MODE) - fc_host_port_type(shost) = FC_PORTTYPE_NPIV; - else - fc_host_port_type(shost) = FC_PORTTYPE_NPORT; break; case FSF_TOPO_AL: - fc_host_port_type(shost) = FC_PORTTYPE_NLPORT; - fc_host_fabric_name(shost) = 0; - fallthrough; default: - fc_host_fabric_name(shost) = 0; dev_err(&adapter->ccw_device->dev, "Unknown or unsupported arbitrated loop " "fibre channel topology detected\n"); @@ -584,13 +556,10 @@ static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req) &adapter->diagnostics->config_data.header; struct fsf_qtcb *qtcb = req->qtcb; struct fsf_qtcb_bottom_config *bottom = &qtcb->bottom.config; - struct Scsi_Host *shost = adapter->scsi_host; if (req->status & ZFCP_STATUS_FSFREQ_ERROR) return; - snprintf(fc_host_firmware_version(shost), FC_VERSION_STRING_SIZE, - "0x%08x", bottom->lic_version); adapter->fsf_lic_version = bottom->lic_version; adapter->adapter_features = bottom->adapter_features; adapter->connection_features = bottom->connection_features; @@ -606,6 +575,7 @@ static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req) */ zfcp_diag_update_xdata(diag_hdr, bottom, false); + zfcp_scsi_shost_update_config_data(adapter, bottom, false); if (zfcp_fsf_exchange_config_evaluate(req)) return; @@ -630,6 +600,8 @@ static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req) &adapter->status); zfcp_fsf_link_down_info_eval(req, &qtcb->header.fsf_status_qual.link_down_info); + + zfcp_scsi_shost_update_config_data(adapter, bottom, true); if (zfcp_fsf_exchange_config_evaluate(req)) return; break; @@ -638,16 +610,8 @@ static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req) return; } - if (adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT) { + if (adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT) adapter->hardware_version = bottom->hardware_version; - snprintf(fc_host_hardware_version(shost), - FC_VERSION_STRING_SIZE, - "0x%08x", bottom->hardware_version); - memcpy(fc_host_serial_number(shost), bottom->serial_number, - min(FC_SERIAL_NUMBER_SIZE, 17)); - EBCASC(fc_host_serial_number(shost), - min(FC_SERIAL_NUMBER_SIZE, 17)); - } if (FSF_QTCB_CURRENT_VERSION < bottom->low_qtcb_version) { dev_err(&adapter->ccw_device->dev, diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 13d873f806e4..e1281a1ce488 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -4,7 +4,7 @@ * * Interface to Linux SCSI midlayer. * - * Copyright IBM Corp. 2002, 2018 + * Copyright IBM Corp. 2002, 2020 */ #define KMSG_COMPONENT "zfcp" @@ -841,6 +841,76 @@ void zfcp_scsi_dif_sense_error(struct scsi_cmnd *scmd, int ascq) set_host_byte(scmd, DID_SOFT_ERROR); } +void zfcp_scsi_shost_update_config_data( + struct zfcp_adapter *const adapter, + const struct fsf_qtcb_bottom_config *const bottom, + const bool bottom_incomplete) +{ + struct Scsi_Host *const shost = adapter->scsi_host; + const struct fc_els_flogi *nsp, *plogi; + + if (shost == NULL) + return; + + snprintf(fc_host_firmware_version(shost), FC_VERSION_STRING_SIZE, + "0x%08x", bottom->lic_version); + + if (adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT) { + snprintf(fc_host_hardware_version(shost), + FC_VERSION_STRING_SIZE, + "0x%08x", bottom->hardware_version); + memcpy(fc_host_serial_number(shost), bottom->serial_number, + min(FC_SERIAL_NUMBER_SIZE, 17)); + EBCASC(fc_host_serial_number(shost), + min(FC_SERIAL_NUMBER_SIZE, 17)); + } + + /* adjust pointers for missing command code */ + nsp = (struct fc_els_flogi *) ((u8 *)&bottom->nport_serv_param + - sizeof(u32)); + plogi = (struct fc_els_flogi *) ((u8 *)&bottom->plogi_payload + - sizeof(u32)); + + snprintf(fc_host_manufacturer(shost), FC_SERIAL_NUMBER_SIZE, "%s", + "IBM"); + fc_host_port_name(shost) = be64_to_cpu(nsp->fl_wwpn); + fc_host_node_name(shost) = be64_to_cpu(nsp->fl_wwnn); + fc_host_supported_classes(shost) = FC_COS_CLASS2 | FC_COS_CLASS3; + + zfcp_scsi_set_prot(adapter); + + /* do not evaluate invalid fields */ + if (bottom_incomplete) + return; + + fc_host_port_id(shost) = ntoh24(bottom->s_id); + fc_host_speed(shost) = + zfcp_fsf_convert_portspeed(bottom->fc_link_speed); + + snprintf(fc_host_model(shost), FC_SYMBOLIC_NAME_SIZE, "0x%04x", + bottom->adapter_type); + + switch (bottom->fc_topology) { + case FSF_TOPO_P2P: + fc_host_port_type(shost) = FC_PORTTYPE_PTP; + fc_host_fabric_name(shost) = 0; + break; + case FSF_TOPO_FABRIC: + fc_host_fabric_name(shost) = be64_to_cpu(plogi->fl_wwnn); + if (bottom->connection_features & FSF_FEATURE_NPIV_MODE) + fc_host_port_type(shost) = FC_PORTTYPE_NPIV; + else + fc_host_port_type(shost) = FC_PORTTYPE_NPORT; + break; + case FSF_TOPO_AL: + fc_host_port_type(shost) = FC_PORTTYPE_NLPORT; + fallthrough; + default: + fc_host_fabric_name(shost) = 0; + break; + } +} + struct fc_function_template zfcp_transport_functions = { .show_starget_port_id = 1, .show_starget_port_name = 1, -- cgit v1.2.3-59-g8ed1b From 52e61fde5ec95cb4011784fb0bc6b436e16fcaa8 Mon Sep 17 00:00:00 2001 From: Benjamin Block Date: Fri, 8 May 2020 19:23:30 +0200 Subject: scsi: zfcp: Move fc_host updates during xport data handling into fenced function When executing exchange port data for a FCP device for the first time, or after an adapter recovery, we update several properties of the fibre channel host object which represents that FCP device. When moving the scsi host object allocation and registration - and thus also the fibre channel host object allocation - to after the first exchange config and exchange port data, this is not possible for the former case. Move all these update into separate, and fenced function that first checks whether the scsi host object already exists or not, before making the updates. During the first ever exchange port data in the adapter life cycle this will make the exchange port data handler skip over this update step, but we can repeat it later, after we allocated the scsi host object. For any further recovery of that adapter the work flow is only changed slightly because then the scsi host object already exists and we don't free it until we release the adapter completely at the end of its life cycle. Link: https://lore.kernel.org/r/ae454c2dc6da0b02907c489af91d0b211d331825.1588956679.git.bblock@linux.ibm.com Reviewed-by: Steffen Maier Signed-off-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_ext.h | 3 +++ drivers/s390/scsi/zfcp_fsf.c | 12 +++--------- drivers/s390/scsi/zfcp_scsi.c | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 9 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 4c4be754cf6e..5dc9bdc5803f 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -176,6 +176,9 @@ extern void zfcp_scsi_shost_update_config_data( struct zfcp_adapter *const adapter, const struct fsf_qtcb_bottom_config *const bottom, const bool bottom_incomplete); +extern void zfcp_scsi_shost_update_port_data( + struct zfcp_adapter *const adapter, + const struct fsf_qtcb_bottom_port *const bottom); /* zfcp_sysfs.c */ extern const struct attribute_group *zfcp_unit_attr_groups[]; diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 54edfcbe84ce..bfb567a1d7bf 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -725,19 +725,10 @@ static void zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *req) { struct zfcp_adapter *adapter = req->adapter; struct fsf_qtcb_bottom_port *bottom = &req->qtcb->bottom.port; - struct Scsi_Host *shost = adapter->scsi_host; if (req->data) memcpy(req->data, bottom, sizeof(*bottom)); - fc_host_permanent_port_name(shost) = bottom->wwpn; - fc_host_maxframe_size(shost) = bottom->maximum_frame_size; - fc_host_supported_speeds(shost) = - zfcp_fsf_convert_portspeed(bottom->supported_speed); - memcpy(fc_host_supported_fc4s(shost), bottom->supported_fc4_types, - FC_FC4_LIST_SIZE); - memcpy(fc_host_active_fc4s(shost), bottom->active_fc4_types, - FC_FC4_LIST_SIZE); if (adapter->adapter_features & FSF_FEATURE_FC_SECURITY) adapter->fc_security_algorithms = bottom->fc_security_algorithms; @@ -764,6 +755,7 @@ static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req) */ zfcp_diag_update_xdata(diag_hdr, bottom, false); + zfcp_scsi_shost_update_port_data(req->adapter, bottom); zfcp_fsf_exchange_port_evaluate(req); break; case FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE: @@ -772,6 +764,8 @@ static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req) zfcp_fsf_link_down_info_eval(req, &qtcb->header.fsf_status_qual.link_down_info); + + zfcp_scsi_shost_update_port_data(req->adapter, bottom); zfcp_fsf_exchange_port_evaluate(req); break; } diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index e1281a1ce488..f98e4015a0ed 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -911,6 +911,25 @@ void zfcp_scsi_shost_update_config_data( } } +void zfcp_scsi_shost_update_port_data( + struct zfcp_adapter *const adapter, + const struct fsf_qtcb_bottom_port *const bottom) +{ + struct Scsi_Host *const shost = adapter->scsi_host; + + if (shost == NULL) + return; + + fc_host_permanent_port_name(shost) = bottom->wwpn; + fc_host_maxframe_size(shost) = bottom->maximum_frame_size; + fc_host_supported_speeds(shost) = + zfcp_fsf_convert_portspeed(bottom->supported_speed); + memcpy(fc_host_supported_fc4s(shost), bottom->supported_fc4_types, + FC_FC4_LIST_SIZE); + memcpy(fc_host_active_fc4s(shost), bottom->active_fc4_types, + FC_FC4_LIST_SIZE); +} + struct fc_function_template zfcp_transport_functions = { .show_starget_port_id = 1, .show_starget_port_name = 1, -- cgit v1.2.3-59-g8ed1b From 990486f3a8508494dab2a7ff0fcc3eb977557d89 Mon Sep 17 00:00:00 2001 From: Benjamin Block Date: Fri, 8 May 2020 19:23:31 +0200 Subject: scsi: zfcp: Fence fc_host updates during link-down handling When receiving a notification that a FCP device lost its local link we usually update the fibre channel host object which represents that FCP device to reflect that. This notification/information can also surface when the FCP device is running through adapter recovery (exchange config and exchange port data return incomplete). When moving the scsi host object allocation and registration - and thus also the fibre channel host object allocation - to after the first exchange config and exchange port data, and this happens during the very first adapter recovery, these updates can not be done until after the scsi host object is allocated. Reorder the fc_host updates in zfcp_fsf_fc_host_link_down() so that they only happen after a check of whether the scsi host object is already allocated or not. During the first adapter recovery this will cause the skip of these updates if a link-down condition is detected, but we can repeat them after we allocated the scsi host object, if necessary. For any further link-down handling the only changes in the work flow are the slightly reordered assignments in zfcp_fsf_fc_host_link_down(). Link: https://lore.kernel.org/r/f841f2cda61dcd7b8549910c44e1831927459edf.1588956679.git.bblock@linux.ibm.com Reviewed-by: Steffen Maier Signed-off-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_fsf.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index bfb567a1d7bf..8c4b690e329e 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -124,17 +124,21 @@ static void zfcp_fsf_fc_host_link_down(struct zfcp_adapter *adapter) { struct Scsi_Host *shost = adapter->scsi_host; + adapter->hydra_version = 0; + adapter->peer_wwpn = 0; + adapter->peer_wwnn = 0; + adapter->peer_d_id = 0; + + /* if there is no shost yet, we have nothing to zero-out */ + if (shost == NULL) + return; + fc_host_port_id(shost) = 0; fc_host_fabric_name(shost) = 0; fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN; - adapter->hydra_version = 0; snprintf(fc_host_model(shost), FC_SYMBOLIC_NAME_SIZE, "0x%04x", 0); memset(fc_host_active_fc4s(shost), 0, FC_FC4_LIST_SIZE); - - adapter->peer_wwpn = 0; - adapter->peer_wwnn = 0; - adapter->peer_d_id = 0; } static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *req, -- cgit v1.2.3-59-g8ed1b From ac007adc4d2d9258fdf27abd25cc77a4e0e8d19f Mon Sep 17 00:00:00 2001 From: Benjamin Block Date: Fri, 8 May 2020 19:23:32 +0200 Subject: scsi: zfcp: Move p-t-p port allocation to after xport data When doing the very first adapter recovery - initialization - for a FCP device in a point-to-point topology we also allocate the port object corresponding to the attached remote port, and trigger a port recovery for it that will run after the adapter recovery finished. Right now this happens right after we finished with the exchange config data command, and uses the fibre channel host object corresponding to the FCP device to determine whether a point-to-point topology is used. When moving the scsi host object allocation and registration - and thus also the fibre channel host object allocation - to after the first exchange config and exchange port data, this use of the fc_host object is not possible anymore at that point in the work flow. But the allocation and recovery trigger doesn't have notable side-effects on the following exchange port data processing, so we can move those to after xport data, and thus also to after the scsi host object allocation, once we move it. Then the fc_host object can be used again, like it is now. For any further adapter recoveries this doesn't change anything, because at that point the port object already exists and recovery is triggered elsewhere for existing port objects. Link: https://lore.kernel.org/r/73e5d4ac21e2b37bf0c3ca8e530bc5a5c6e74f8f.1588956679.git.bblock@linux.ibm.com Reviewed-by: Steffen Maier Signed-off-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_erp.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 3d0bc000f500..356f51d2bbee 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -4,7 +4,7 @@ * * Error Recovery Procedures (ERP). * - * Copyright IBM Corp. 2002, 2017 + * Copyright IBM Corp. 2002, 2020 */ #define KMSG_COMPONENT "zfcp" @@ -768,10 +768,14 @@ static enum zfcp_erp_act_result zfcp_erp_adapter_strat_fsf_xconf( if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_XCONFIG_OK)) return ZFCP_ERP_FAILED; + return ZFCP_ERP_SUCCEEDED; +} + +static void +zfcp_erp_adapter_strategy_open_ptp_port(struct zfcp_adapter *const adapter) +{ if (fc_host_port_type(adapter->scsi_host) == FC_PORTTYPE_PTP) zfcp_erp_enqueue_ptp_port(adapter); - - return ZFCP_ERP_SUCCEEDED; } static enum zfcp_erp_act_result zfcp_erp_adapter_strategy_open_fsf_xport( @@ -809,6 +813,8 @@ static enum zfcp_erp_act_result zfcp_erp_adapter_strategy_open_fsf( if (zfcp_erp_adapter_strategy_open_fsf_xport(act) == ZFCP_ERP_FAILED) return ZFCP_ERP_FAILED; + zfcp_erp_adapter_strategy_open_ptp_port(act->adapter); + if (mempool_resize(act->adapter->pool.sr_data, act->adapter->stat_read_buf_num)) return ZFCP_ERP_FAILED; -- cgit v1.2.3-59-g8ed1b From 971f2abb4ca4095bb4bd7c2ba119d1cca078b433 Mon Sep 17 00:00:00 2001 From: Benjamin Block Date: Fri, 8 May 2020 19:23:33 +0200 Subject: scsi: zfcp: Fence adapter status propagation for common statuses Common status flags that all main objects - adapter, port, and unit - support are propagated to sub-objects when set or cleared. For instance, when setting the status ZFCP_STATUS_COMMON_ERP_INUSE for an adapter object, we will propagate this to all its child ports and units - same for when clearing a common status flag. Units of an adapter object are enumerated via __shost_for_each_device() over the scsi host object of the corresponding adapter. Once we move the scsi host object allocation and registration to after the first exchange config and exchange port data, this won't be possible for cases where we set or clear common statuses during the very first adapter recovery. But since we won't have any port or unit objects yet at that point of time, we can just fence the status propagation for cases where the scsi host object is not yet set in the adapter object. It won't change any effective status propagations, but will prevent us from dereferencing invalid pointers. For any later point in the work flow the scsi host object will be set and thus nothing is changed then. Link: https://lore.kernel.org/r/f51fe5f236a1e3d1ce53379c308777561bfe35e1.1588956679.git.bblock@linux.ibm.com Reviewed-by: Steffen Maier Signed-off-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_erp.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 356f51d2bbee..283bcb985655 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -1642,6 +1642,13 @@ void zfcp_erp_set_adapter_status(struct zfcp_adapter *adapter, u32 mask) atomic_or(common_mask, &port->status); read_unlock_irqrestore(&adapter->port_list_lock, flags); + /* + * if `scsi_host` is missing, xconfig/xport data has never completed + * yet, so we can't access it, but there are also no SDEVs yet + */ + if (adapter->scsi_host == NULL) + return; + spin_lock_irqsave(adapter->scsi_host->host_lock, flags); __shost_for_each_device(sdev, adapter->scsi_host) atomic_or(common_mask, &sdev_to_zfcp(sdev)->status); @@ -1679,6 +1686,13 @@ void zfcp_erp_clear_adapter_status(struct zfcp_adapter *adapter, u32 mask) } read_unlock_irqrestore(&adapter->port_list_lock, flags); + /* + * if `scsi_host` is missing, xconfig/xport data has never completed + * yet, so we can't access it, but there are also no SDEVs yet + */ + if (adapter->scsi_host == NULL) + return; + spin_lock_irqsave(adapter->scsi_host->host_lock, flags); __shost_for_each_device(sdev, adapter->scsi_host) { atomic_andnot(common_mask, &sdev_to_zfcp(sdev)->status); -- cgit v1.2.3-59-g8ed1b From 71159b6ecb067565fb41faba616116e8c0fc10ea Mon Sep 17 00:00:00 2001 From: Benjamin Block Date: Fri, 8 May 2020 19:23:34 +0200 Subject: scsi: zfcp: Fence early sysfs interfaces for accesses of shost objects When setting an adapter online for the first time, we also create a couple of entries for it in the sysfs device tree. This is also true even if the adapter has not yet ever gone successfully through exchange config and exchange port data. When moving the scsi host object allocation and registration to after the first exchange config and exchange port data, this make the `port_rescan` attribute susceptible to invalid pointer-dereferences of the shost field before the adapter is fully initialized. When written to, it schedules a `scan_work` item that will in turn make use of the associated fibre channel host object to check the topology used for this FCP device. Because scanning for remote ports can't be done successfully without completing exchange config and exchange port data first, we can simply fence `port_rescan`, and so prevent the illegal access. As with cases where we can't get a reference to the adapter, we also return -ENODEV here. Applications need to handle that errno today already. After a successful allocation of the scsi host object nothing changes in the work flow. Link: https://lore.kernel.org/r/ef65366d309993ca91b6917727590ca7ca166c8f.1588956679.git.bblock@linux.ibm.com Reviewed-by: Steffen Maier Signed-off-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_sysfs.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index 7ec30ded0169..8d9662e8b717 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -216,10 +216,22 @@ static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev, { struct ccw_device *cdev = to_ccwdev(dev); struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); + int retval = 0; if (!adapter) return -ENODEV; + /* + * If `scsi_host` is missing, we can't schedule `scan_work`, as it + * makes use of the corresponding fc_host object. But this state is + * only possible if xconfig/xport data has never completed yet, + * and we couldn't successfully scan for ports anyway. + */ + if (adapter->scsi_host == NULL) { + retval = -ENODEV; + goto out; + } + /* * Users wish is our command: immediately schedule and flush a * worker to conduct a synchronous port scan, that is, neither @@ -227,9 +239,9 @@ static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev, */ queue_delayed_work(adapter->work_queue, &adapter->scan_work, 0); flush_delayed_work(&adapter->scan_work); +out: zfcp_ccw_adapter_put(adapter); - - return (ssize_t) count; + return retval ? retval : (ssize_t) count; } static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL, zfcp_sysfs_port_rescan_store); -- cgit v1.2.3-59-g8ed1b From d0dff2ac98dd41d7d451127d9eae2f6478fc40b0 Mon Sep 17 00:00:00 2001 From: Benjamin Block Date: Fri, 8 May 2020 19:23:35 +0200 Subject: scsi: zfcp: Move allocation of the shost object to after xconf- and xport-data At the moment we allocate and register the Scsi_Host object corresponding to a zfcp adapter (FCP device) very early in the life cycle of the adapter - even before we fully discover and initialize the underlying firmware/hardware. This had the advantage that we could already use the Scsi_Host object, and fill in all its information during said discover and initialize. Due to commit 737eb78e82d5 ("block: Delay default elevator initialization") (first released in v5.4), we noticed a regression that would prevent us from using any storage volume if zfcp is configured with support for DIF or DIX (zfcp.dif=1 || zfcp.dix=1). Doing so would result in an illegal memory access as soon as the first request is sent with such an configuration. As example for a crash resulting from this: scsi host0: scsi_eh_0: sleeping scsi host0: zfcp qdio: 0.0.1900 ZFCP on SC 4bd using AI:1 QEBSM:0 PRI:1 TDD:1 SIGA: W AP scsi 0:0:0:0: scsi scan: INQUIRY pass 1 length 36 Unable to handle kernel pointer dereference in virtual kernel address space Failing address: 0000000000000000 TEID: 0000000000000483 Fault in home space mode while using kernel ASCE. AS:0000000035c7c007 R3:00000001effcc007 S:00000001effd1000 P:000000000000003d Oops: 0004 ilc:3 [#1] PREEMPT SMP DEBUG_PAGEALLOC Modules linked in: ... CPU: 1 PID: 783 Comm: kworker/u760:5 Kdump: loaded Not tainted 5.6.0-rc2-bb-next+ #1 Hardware name: ... Workqueue: scsi_wq_0 fc_scsi_scan_rport [scsi_transport_fc] Krnl PSW : 0704e00180000000 000003ff801fcdae (scsi_queue_rq+0x436/0x740 [scsi_mod]) R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:3 CC:2 PM:0 RI:0 EA:3 Krnl GPRS: 0fffffffffffffff 0000000000000000 0000000187150120 0000000000000000 000003ff80223d20 000000000000018e 000000018adc6400 0000000187711000 000003e0062337e8 00000001ae719000 0000000187711000 0000000187150000 00000001ab808100 0000000187150120 000003ff801fcd74 000003e0062336a0 Krnl Code: 000003ff801fcd9e: e310a35c0012 lt %r1,860(%r10) 000003ff801fcda4: a7840010 brc 8,000003ff801fcdc4 #000003ff801fcda8: e310b2900004 lg %r1,656(%r11) >000003ff801fcdae: d71710001000 xc 0(24,%r1),0(%r1) 000003ff801fcdb4: e310b2900004 lg %r1,656(%r11) 000003ff801fcdba: 41201018 la %r2,24(%r1) 000003ff801fcdbe: e32010000024 stg %r2,0(%r1) 000003ff801fcdc4: b904002b lgr %r2,%r11 Call Trace: [<000003ff801fcdae>] scsi_queue_rq+0x436/0x740 [scsi_mod] ([<000003ff801fcd74>] scsi_queue_rq+0x3fc/0x740 [scsi_mod]) [<00000000349c9970>] blk_mq_dispatch_rq_list+0x390/0x680 [<00000000349d1596>] blk_mq_sched_dispatch_requests+0x196/0x1a8 [<00000000349c7a04>] __blk_mq_run_hw_queue+0x144/0x160 [<00000000349c7ab6>] __blk_mq_delay_run_hw_queue+0x96/0x228 [<00000000349c7d5a>] blk_mq_run_hw_queue+0xd2/0xe0 [<00000000349d194a>] blk_mq_sched_insert_request+0x192/0x1d8 [<00000000349c17b8>] blk_execute_rq_nowait+0x80/0x90 [<00000000349c1856>] blk_execute_rq+0x6e/0xb0 [<000003ff801f8ac2>] __scsi_execute+0xe2/0x1f0 [scsi_mod] [<000003ff801fef98>] scsi_probe_and_add_lun+0x358/0x840 [scsi_mod] [<000003ff8020001c>] __scsi_scan_target+0xc4/0x228 [scsi_mod] [<000003ff80200254>] scsi_scan_target+0xd4/0x100 [scsi_mod] [<000003ff802d8b96>] fc_scsi_scan_rport+0x96/0xc0 [scsi_transport_fc] [<0000000034245ce8>] process_one_work+0x458/0x7d0 [<00000000342462a2>] worker_thread+0x242/0x448 [<0000000034250994>] kthread+0x15c/0x170 [<0000000034e1979c>] ret_from_fork+0x30/0x38 INFO: lockdep is turned off. Last Breaking-Event-Address: [<000003ff801fbc36>] scsi_add_cmd_to_list+0x9e/0xa8 [scsi_mod] Kernel panic - not syncing: Fatal exception: panic_on_oops While this issue is exposed by the commit named above, this is only by accident. The real issue exists for longer already - basically since it's possible to use blk-mq via scsi-mq, and blk-mq pre-allocates all requests for a tag-set during initialization of the same. For a given Scsi_Host object this is done when adding the object to the midlayer (`scsi_add_host()` and such). In `scsi_mq_setup_tags()` the midlayer calculates how much memory is required for a single scsi_cmnd, and its additional data, which also might include space for additional protection data - depending on whether the Scsi_Host has any form of protection capabilities (`scsi_host_get_prot()`). The problem is now thus, because zfcp does this step before we actually know whether the firmware/hardware has these capabilities, we don't set any protection capabilities in the Scsi_Host object. And so, no space is allocated for additional protection data for requests in the Scsi_Host tag-set. Once we go through discover and initialize the FCP device firmware/hardware fully (this is done via the firmware commands "Exchange Config Data" and "Exchange Port Data") we find out whether it actually supports DIF and DIX, and we set the corresponding capabilities in the Scsi_Host object (in `zfcp_scsi_set_prot()`). Now the Scsi_Host potentially has protection capabilities, but the already allocated requests in the tag-set don't have any space allocated for that. When we then trigger target scanning or add scsi_devices manually, the midlayer will use requests from that tag-set, and before sending most requests, it will also call `scsi_mq_prep_fn()`. To prepare the scsi_cmnd this function will check again whether the used Scsi_Host has any protection capabilities - and now it potentially has - and if so, it will try to initialize the assumed to be preallocated structures and thus it causes the crash, like shown above. Before delaying the default elevator initialization with the commit named above, we always would also allocate an elevator for any scsi_device before ever sending any requests - in contrast to now, where we do it after device-probing. That elevator in turn would have its own tag-set, and that is initialized after we went through discovery and initialization of the underlying firmware/hardware. So requests from that tag-set can be allocated properly, and if used - unless the user changes/disabled the default elevator - this would hide the underlying issue. To fix this for any configuration - with or without an elevator - we move the allocation and registration of the Scsi_Host object for a given FCP device to after the first complete discovery and initialization of the underlying firmware/hardware. By doing that we can make all basic properties of the Scsi_Host known to the midlayer by the time we call `scsi_add_host()`, including whether we have any protection capabilities. To do that we have to delay all the accesses that we would have done in the past during discovery and initialization, and do them instead once we are finished with it. The previous patches ramp up to this by fencing and factoring out all these accesses, and make it possible to re-do them later on. In addition we make also use of the diagnostic buffers we recently added with commit 92953c6e0aa7 ("scsi: zfcp: signal incomplete or error for sync exchange config/port data") commit 7e418833e689 ("scsi: zfcp: diagnostics buffer caching and use for exchange port data") commit 088210233e6f ("scsi: zfcp: add diagnostics buffer for exchange config data") (first released in v5.5), because these already cache all the information we need for that "re-do operation" - the information cached are always updated during xconf or xport data, so it won't be stale. In addition to the move and re-do, this patch also updates the function-documentation of `zfcp_scsi_adapter_register()` and changes how it reports if a Scsi_Host object already exists. In that case future recovery-operations can skip this step completely and behave much like they would do in the past - zfcp does not release a once allocated Scsi_Host object unless the corresponding FCP device is deconstructed completely. Link: https://lore.kernel.org/r/030dd6da318bbb529f0b5268ec65cebcd20fc0a3.1588956679.git.bblock@linux.ibm.com Reviewed-by: Steffen Maier Signed-off-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_aux.c | 5 ++-- drivers/s390/scsi/zfcp_diag.h | 6 ++--- drivers/s390/scsi/zfcp_erp.c | 58 +++++++++++++++++++++++++++++++++++++++++++ drivers/s390/scsi/zfcp_ext.h | 1 + drivers/s390/scsi/zfcp_fsf.c | 2 +- drivers/s390/scsi/zfcp_scsi.c | 40 ++++++++++++++++++++++------- 6 files changed, 96 insertions(+), 16 deletions(-) (limited to 'drivers/s390') diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 09ec846fe01d..18b713a616de 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -4,7 +4,7 @@ * * Module interface and handling of zfcp data structures. * - * Copyright IBM Corp. 2002, 2018 + * Copyright IBM Corp. 2002, 2020 */ /* @@ -415,8 +415,7 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device) adapter->stat_read_buf_num = FSF_STATUS_READS_RECOM; - if (!zfcp_scsi_adapter_register(adapter)) - return adapter; + return adapter; failed: zfcp_adapter_unregister(adapter); diff --git a/drivers/s390/scsi/zfcp_diag.h b/drivers/s390/scsi/zfcp_diag.h index b9c93d15f67c..3852367f15f6 100644 --- a/drivers/s390/scsi/zfcp_diag.h +++ b/drivers/s390/scsi/zfcp_diag.h @@ -4,7 +4,7 @@ * * Definitions for handling diagnostics in the the zfcp device driver. * - * Copyright IBM Corp. 2018 + * Copyright IBM Corp. 2018, 2020 */ #ifndef ZFCP_DIAG_H @@ -56,11 +56,11 @@ struct zfcp_diag_adapter { unsigned long max_age; - struct { + struct zfcp_diag_adapter_port_data { struct zfcp_diag_header header; struct fsf_qtcb_bottom_port data; } port_data; - struct { + struct zfcp_diag_adapter_config_data { struct zfcp_diag_header header; struct fsf_qtcb_bottom_config data; } config_data; diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 283bcb985655..db320dab1fee 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -14,6 +14,7 @@ #include #include "zfcp_ext.h" #include "zfcp_reqlist.h" +#include "zfcp_diag.h" #define ZFCP_MAX_ERPS 3 @@ -804,6 +805,59 @@ static enum zfcp_erp_act_result zfcp_erp_adapter_strategy_open_fsf_xport( return ZFCP_ERP_SUCCEEDED; } +static enum zfcp_erp_act_result +zfcp_erp_adapter_strategy_alloc_shost(struct zfcp_adapter *const adapter) +{ + struct zfcp_diag_adapter_config_data *const config_data = + &adapter->diagnostics->config_data; + struct zfcp_diag_adapter_port_data *const port_data = + &adapter->diagnostics->port_data; + unsigned long flags; + int rc; + + rc = zfcp_scsi_adapter_register(adapter); + if (rc == -EEXIST) + return ZFCP_ERP_SUCCEEDED; + else if (rc) + return ZFCP_ERP_FAILED; + + /* + * We allocated the shost for the first time. Before it was NULL, + * and so we deferred all updates in the xconf- and xport-data + * handlers. We need to make up for that now, and make all the updates + * that would have been done before. + * + * We can be sure that xconf- and xport-data succeeded, because + * otherwise this function is not called. But they might have been + * incomplete. + */ + + spin_lock_irqsave(&config_data->header.access_lock, flags); + zfcp_scsi_shost_update_config_data(adapter, &config_data->data, + !!config_data->header.incomplete); + spin_unlock_irqrestore(&config_data->header.access_lock, flags); + + if (adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT) { + spin_lock_irqsave(&port_data->header.access_lock, flags); + zfcp_scsi_shost_update_port_data(adapter, &port_data->data); + spin_unlock_irqrestore(&port_data->header.access_lock, flags); + } + + /* + * There is a remote possibility that the 'Exchange Port Data' request + * reports a different connectivity status than 'Exchange Config Data'. + * But any change to the connectivity status of the local optic that + * happens after the initial xconf request is expected to be reported + * to us, as soon as we post Status Read Buffers to the FCP channel + * firmware after this function. So any resulting inconsistency will + * only be momentary. + */ + if (config_data->header.incomplete) + zfcp_fsf_fc_host_link_down(adapter); + + return ZFCP_ERP_SUCCEEDED; +} + static enum zfcp_erp_act_result zfcp_erp_adapter_strategy_open_fsf( struct zfcp_erp_action *act) { @@ -813,6 +867,10 @@ static enum zfcp_erp_act_result zfcp_erp_adapter_strategy_open_fsf( if (zfcp_erp_adapter_strategy_open_fsf_xport(act) == ZFCP_ERP_FAILED) return ZFCP_ERP_FAILED; + if (zfcp_erp_adapter_strategy_alloc_shost(act->adapter) == + ZFCP_ERP_FAILED) + return ZFCP_ERP_FAILED; + zfcp_erp_adapter_strategy_open_ptp_port(act->adapter); if (mempool_resize(act->adapter->pool.sr_data, diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 5dc9bdc5803f..3ef5d74331c3 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -135,6 +135,7 @@ extern int zfcp_fsf_send_els(struct zfcp_adapter *, u32, struct zfcp_fsf_ct_els *, unsigned int); extern int zfcp_fsf_fcp_cmnd(struct scsi_cmnd *); extern void zfcp_fsf_req_free(struct zfcp_fsf_req *); +extern void zfcp_fsf_fc_host_link_down(struct zfcp_adapter *adapter); extern struct zfcp_fsf_req *zfcp_fsf_fcp_task_mgmt(struct scsi_device *sdev, u8 tm_flags); extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_cmnd(struct scsi_cmnd *); diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 8c4b690e329e..c795f22249d8 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -120,7 +120,7 @@ static void zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *req) read_unlock_irqrestore(&adapter->port_list_lock, flags); } -static void zfcp_fsf_fc_host_link_down(struct zfcp_adapter *adapter) +void zfcp_fsf_fc_host_link_down(struct zfcp_adapter *adapter) { struct Scsi_Host *shost = adapter->scsi_host; diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index f98e4015a0ed..d58bf79892f2 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -451,26 +451,39 @@ static struct scsi_host_template zfcp_scsi_host_template = { }; /** - * zfcp_scsi_adapter_register - Register SCSI and FC host with SCSI midlayer + * zfcp_scsi_adapter_register() - Allocate and register SCSI and FC host with + * SCSI midlayer * @adapter: The zfcp adapter to register with the SCSI midlayer + * + * Allocates the SCSI host object for the given adapter, sets basic properties + * (such as the transport template, QDIO limits, ...), and registers it with + * the midlayer. + * + * During registration with the midlayer the corresponding FC host object for + * the referenced transport class is also implicitely allocated. + * + * Upon success adapter->scsi_host is set, and upon failure it remains NULL. If + * adapter->scsi_host is already set, nothing is done. + * + * Return: + * * 0 - Allocation and registration was successful + * * -EEXIST - SCSI and FC host did already exist, nothing was done, nothing + * was changed + * * -EIO - Allocation or registration failed */ int zfcp_scsi_adapter_register(struct zfcp_adapter *adapter) { struct ccw_dev_id dev_id; if (adapter->scsi_host) - return 0; + return -EEXIST; ccw_device_get_id(adapter->ccw_device, &dev_id); /* register adapter as SCSI host with mid layer of SCSI stack */ adapter->scsi_host = scsi_host_alloc(&zfcp_scsi_host_template, sizeof (struct zfcp_adapter *)); - if (!adapter->scsi_host) { - dev_err(&adapter->ccw_device->dev, - "Registering the FCP device with the " - "SCSI stack failed\n"); - return -EIO; - } + if (!adapter->scsi_host) + goto err_out; /* tell the SCSI stack some characteristics of this adapter */ adapter->scsi_host->max_id = 511; @@ -480,14 +493,23 @@ int zfcp_scsi_adapter_register(struct zfcp_adapter *adapter) adapter->scsi_host->max_cmd_len = 16; /* in struct fcp_cmnd */ adapter->scsi_host->transportt = zfcp_scsi_transport_template; + /* make all basic properties known at registration time */ + zfcp_qdio_shost_update(adapter, adapter->qdio); + zfcp_scsi_set_prot(adapter); + adapter->scsi_host->hostdata[0] = (unsigned long) adapter; if (scsi_add_host(adapter->scsi_host, &adapter->ccw_device->dev)) { scsi_host_put(adapter->scsi_host); - return -EIO; + goto err_out; } return 0; +err_out: + adapter->scsi_host = NULL; + dev_err(&adapter->ccw_device->dev, + "Registering the FCP device with the SCSI stack failed\n"); + return -EIO; } /** -- cgit v1.2.3-59-g8ed1b