scsi: zfcp: fix fc_host attributes that should be unknown on local link down
authorSteffen Maier <maier@linux.ibm.com>
Thu, 12 Mar 2020 17:44:59 +0000 (18:44 +0100)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 17 Mar 2020 17:12:36 +0000 (13:12 -0400)
When we get an unsolicited notification on local link went down,
zfcp_fsf_status_read_link_down() calls zfcp_fsf_link_down_info_eval().
This only blocks rports, and sets ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED and
ZFCP_STATUS_COMMON_ERP_FAILED. Only the fc_host port_state changes to
"Linkdown", because zfcp_scsi_get_host_port_state() is an active callback
and uses the adapter status.

Other fc_host attributes model, port_id, port_type, speed, fabric_name (and
zfcp device attributes card_version, peer_wwpn, peer_wwnn, peer_d_id) which
depend on a local link, continued to show their last known "good" value.

Only if something triggered an exchange config data, some values were
updated to their unknown equivalent via case
FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE due to local link down.  Triggers for
exchange config data are adapter recovery, or reading any of the following
zfcp-specific scsi host sysfs attributes "requests", "megabytes", or
"seconds_active" in /sys/devices/css*/*.*.*/*.*.*/host*/scsi_host/host*/.

The other fc_host attributes active_fc4s and permanent_port_name continued
to show their last known "good" value.  Only if something triggered an
exchange port data, some values changed.  Active_fc4s became all zeros as
unknown equivalent during link down.  Permanent_port_name does not depend
on a local link. But for non-NPIV FCP devices, permanent_port_name
erroneously became whatever value fc_host port_name had at that point in
time (see previous paragraph).  Triggers for exchange port data are the
zfcp-specific scsi host sysfs attribute "utilization", or
[{reset,get}_fc_host_stats] write anything into "reset_statistics" or read
any of the other attributes under
/sys/devices/css*/*.*.*/*.*.*/host*/fc_host/host*/statistics/.

(cf. v4.9 commit bd77befa5bcf ("zfcp: fix fc_host port_type with NPIV"))

This is particularly confusing when using "lszfcp -b <fcpdevbusid> -Ha" or
dbginfo.sh which read fc_host attributes and also scsi_host attributes.
After link down, the first invocation produces (abbreviated):

Class = "fc_host"
    active_fc4s         = "0x00 0x00 0x01 0x00 ..."
    ...
    fabric_name         = "0x10000027f8e04c49"
    ...
    permanent_port_name = "0xc05076e4588059c1"
    port_id             = "0x244800"
    port_state          = "Linkdown"
    port_type           = "NPort (fabric via point-to-point)"
    ...
    speed               = "16 Gbit"
Class = "scsi_host"
    ...
    megabytes           = "0 0"
    ...
    requests            = "0 0 0"
    seconds_active      = "37"
    ...
    utilization         = "0 0 0"

The second and next invocations produce (abbreviated):

Class = "fc_host"
    active_fc4s         = "0x00 0x00 0x00 0x00 ..."
    ...
    fabric_name         = "0x0"
    ...
    permanent_port_name = "0x0"
    port_id             = "0x000000"
    port_state          = "Linkdown"
    port_type           = "Unknown"
    ...
    speed               = "unknown"
Class = "scsi_host"
    ...
    megabytes           = "0 0"
    ...
    requests            = "0 0 0"
    seconds_active      = "38"
    ...
    utilization         = "0 0 0"

Factor out the resetting of local link dependent fc_host attributes from
zfcp_fsf_exchange_config_data_handler() case
FSF_EXCHANGE_CONFIG_DATA_INCOMPLETE into a new helper function
zfcp_fsf_fc_host_link_down().  All code places that detect local link down
(SRB, FSF_PROT_LINK_DOWN, xconf data/port incomplete) call
zfcp_fsf_link_down_info_eval().  Call the new helper from there. This works
because zfcp_fsf_link_down_info_eval() and thus the helper is called before
zfcp_fsf_exchange_{config,port}_evaluate().

Port_name and node_name are always valid, so never reset them.

Get the permanent_port_name from exchange port data unconditionally as it
always has a valid known good value, even during link down.

Note: Rather than hardcode in zfcp_fsf_exchange_config_evaluate(), fc_host
supported_classes could theoretically get its value from
fsf_qtcb_bottom_port.class_of_service in zfcp_fsf_exchange_port_evaluate().

When the link comes back, we get a different notification, perform adapter
recovery, and this triggers an implicit exchange config data followed by
exchange port data filling in the link dependent fc_host attributes with
known good values again.

Link: https://lore.kernel.org/r/20200312174505.51294-5-maier@linux.ibm.com
Reviewed-by: Jens Remus <jremus@linux.ibm.com>
Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
Signed-off-by: Steffen Maier <maier@linux.ibm.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/s390/scsi/zfcp_fsf.c

index 31ecbc1..1fa9427 100644 (file)
@@ -120,6 +120,23 @@ 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)
+{
+       struct Scsi_Host *shost = adapter->scsi_host;
+
+       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,
                                         struct fsf_link_down_info *link_down)
 {
@@ -132,6 +149,8 @@ static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *req,
 
        zfcp_scsi_schedule_rports_block(adapter);
 
+       zfcp_fsf_fc_host_link_down(adapter);
+
        if (!link_down)
                goto out;
 
@@ -512,9 +531,6 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
        adapter->stat_read_buf_num = max(bottom->status_read_buf_num,
                                         (u16)FSF_STATUS_READS_RECOM);
 
-       if (fc_host_permanent_port_name(shost) == -1)
-               fc_host_permanent_port_name(shost) = fc_host_port_name(shost);
-
        zfcp_scsi_set_prot(adapter);
 
        /* no error return above here, otherwise must fix call chains */
@@ -608,16 +624,6 @@ static void zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *req)
                zfcp_diag_update_xdata(diag_hdr, bottom, true);
                req->status |= ZFCP_STATUS_FSFREQ_XDATAINCOMPLETE;
 
-               fc_host_node_name(shost) = 0;
-               fc_host_port_name(shost) = 0;
-               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);
-
                /* avoids adapter shutdown to be able to recognize
                 * events such as LINK UP */
                atomic_or(ZFCP_STATUS_ADAPTER_XCONFIG_OK,
@@ -667,10 +673,7 @@ static void zfcp_fsf_exchange_port_evaluate(struct zfcp_fsf_req *req)
        if (req->data)
                memcpy(req->data, bottom, sizeof(*bottom));
 
-       if (adapter->connection_features & FSF_FEATURE_NPIV_MODE) {
-               fc_host_permanent_port_name(shost) = bottom->wwpn;
-       } else
-               fc_host_permanent_port_name(shost) = fc_host_port_name(shost);
+       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);
@@ -704,9 +707,9 @@ static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req)
                zfcp_diag_update_xdata(diag_hdr, bottom, true);
                req->status |= ZFCP_STATUS_FSFREQ_XDATAINCOMPLETE;
 
-               zfcp_fsf_exchange_port_evaluate(req);
                zfcp_fsf_link_down_info_eval(req,
                        &qtcb->header.fsf_status_qual.link_down_info);
+               zfcp_fsf_exchange_port_evaluate(req);
                break;
        }
 }