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; } /**