scsi: fc: Parse FPIN packets and update statistics
authorShyam Sundar <ssundar@marvell.com>
Wed, 21 Oct 2020 09:27:13 +0000 (02:27 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Mon, 26 Oct 2020 22:06:45 +0000 (18:06 -0400)
Parse the incoming FPIN packets and update the host and rport FPIN
statistics based on the FPINs.

Link: https://lore.kernel.org/r/20201021092715.22669-4-njavali@marvell.com
Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com>
Reviewed-by: James Smart <james.smart@broadcom.com>
Signed-off-by: Shyam Sundar <ssundar@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/scsi_transport_fc.c
include/scsi/scsi_transport_fc.h

index 501e165..4dfa0e4 100644 (file)
@@ -34,6 +34,11 @@ static int fc_bsg_hostadd(struct Scsi_Host *, struct fc_host_attrs *);
 static int fc_bsg_rportadd(struct Scsi_Host *, struct fc_rport *);
 static void fc_bsg_remove(struct request_queue *);
 static void fc_bsg_goose_queue(struct fc_rport *);
+static void fc_li_stats_update(struct fc_fn_li_desc *li_desc,
+                              struct fc_fpin_stats *stats);
+static void fc_delivery_stats_update(u32 reason_code,
+                                    struct fc_fpin_stats *stats);
+static void fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats);
 
 /*
  * Module Parameters
@@ -631,6 +636,262 @@ fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
 EXPORT_SYMBOL(fc_host_post_vendor_event);
 
 /**
+ * fc_find_rport_by_wwpn - find the fc_rport pointer for a given wwpn
+ * @shost:             host the fc_rport is associated with
+ * @wwpn:              wwpn of the fc_rport device
+ *
+ * Notes:
+ *     This routine assumes no locks are held on entry.
+ */
+struct fc_rport *
+fc_find_rport_by_wwpn(struct Scsi_Host *shost, u64 wwpn)
+{
+       struct fc_rport *rport;
+       unsigned long flags;
+
+       spin_lock_irqsave(shost->host_lock, flags);
+
+       list_for_each_entry(rport, &fc_host_rports(shost), peers) {
+               if (rport->port_state != FC_PORTSTATE_ONLINE)
+                       continue;
+
+               if (rport->port_name == wwpn) {
+                       spin_unlock_irqrestore(shost->host_lock, flags);
+                       return rport;
+               }
+       }
+
+       spin_unlock_irqrestore(shost->host_lock, flags);
+       return NULL;
+}
+EXPORT_SYMBOL(fc_find_rport_by_wwpn);
+
+static void
+fc_li_stats_update(struct fc_fn_li_desc *li_desc,
+                  struct fc_fpin_stats *stats)
+{
+       stats->li += be32_to_cpu(li_desc->event_count);
+       switch (be16_to_cpu(li_desc->event_type)) {
+       case FPIN_LI_UNKNOWN:
+               stats->li_failure_unknown +=
+                   be32_to_cpu(li_desc->event_count);
+               break;
+       case FPIN_LI_LINK_FAILURE:
+               stats->li_link_failure_count +=
+                   be32_to_cpu(li_desc->event_count);
+               break;
+       case FPIN_LI_LOSS_OF_SYNC:
+               stats->li_loss_of_sync_count +=
+                   be32_to_cpu(li_desc->event_count);
+               break;
+       case FPIN_LI_LOSS_OF_SIG:
+               stats->li_loss_of_signals_count +=
+                   be32_to_cpu(li_desc->event_count);
+               break;
+       case FPIN_LI_PRIM_SEQ_ERR:
+               stats->li_prim_seq_err_count +=
+                   be32_to_cpu(li_desc->event_count);
+               break;
+       case FPIN_LI_INVALID_TX_WD:
+               stats->li_invalid_tx_word_count +=
+                   be32_to_cpu(li_desc->event_count);
+               break;
+       case FPIN_LI_INVALID_CRC:
+               stats->li_invalid_crc_count +=
+                   be32_to_cpu(li_desc->event_count);
+               break;
+       case FPIN_LI_DEVICE_SPEC:
+               stats->li_device_specific +=
+                   be32_to_cpu(li_desc->event_count);
+               break;
+       }
+}
+
+static void
+fc_delivery_stats_update(u32 reason_code, struct fc_fpin_stats *stats)
+{
+       stats->dn++;
+       switch (reason_code) {
+       case FPIN_DELI_UNKNOWN:
+               stats->dn_unknown++;
+               break;
+       case FPIN_DELI_TIMEOUT:
+               stats->dn_timeout++;
+               break;
+       case FPIN_DELI_UNABLE_TO_ROUTE:
+               stats->dn_unable_to_route++;
+               break;
+       case FPIN_DELI_DEVICE_SPEC:
+               stats->dn_device_specific++;
+               break;
+       }
+}
+
+static void
+fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats)
+{
+       stats->cn++;
+       switch (event_type) {
+       case FPIN_CONGN_CLEAR:
+               stats->cn_clear++;
+               break;
+       case FPIN_CONGN_LOST_CREDIT:
+               stats->cn_lost_credit++;
+               break;
+       case FPIN_CONGN_CREDIT_STALL:
+               stats->cn_credit_stall++;
+               break;
+       case FPIN_CONGN_OVERSUBSCRIPTION:
+               stats->cn_oversubscription++;
+               break;
+       case FPIN_CONGN_DEVICE_SPEC:
+               stats->cn_device_specific++;
+       }
+}
+
+/*
+ * fc_fpin_li_stats_update - routine to update Link Integrity
+ * event statistics.
+ * @shost:             host the FPIN was received on
+ * @tlv:               pointer to link integrity descriptor
+ *
+ */
+static void
+fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv)
+{
+       u8 i;
+       struct fc_rport *rport = NULL;
+       struct fc_rport *attach_rport = NULL;
+       struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
+       struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv;
+       u64 wwpn;
+
+       rport = fc_find_rport_by_wwpn(shost,
+                                     be64_to_cpu(li_desc->attached_wwpn));
+       if (rport &&
+           (rport->roles & FC_PORT_ROLE_FCP_TARGET ||
+            rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
+               attach_rport = rport;
+               fc_li_stats_update(li_desc, &attach_rport->fpin_stats);
+       }
+
+       if (be32_to_cpu(li_desc->pname_count) > 0) {
+               for (i = 0;
+                   i < be32_to_cpu(li_desc->pname_count);
+                   i++) {
+                       wwpn = be64_to_cpu(li_desc->pname_list[i]);
+                       rport = fc_find_rport_by_wwpn(shost, wwpn);
+                       if (rport &&
+                           (rport->roles & FC_PORT_ROLE_FCP_TARGET ||
+                           rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
+                               if (rport == attach_rport)
+                                       continue;
+                               fc_li_stats_update(li_desc,
+                                                  &rport->fpin_stats);
+                       }
+               }
+       }
+
+       if (fc_host->port_name == be64_to_cpu(li_desc->attached_wwpn))
+               fc_li_stats_update(li_desc, &fc_host->fpin_stats);
+}
+
+/*
+ * fc_fpin_delivery_stats_update - routine to update Delivery Notification
+ * event statistics.
+ * @shost:             host the FPIN was received on
+ * @tlv:               pointer to delivery descriptor
+ *
+ */
+static void
+fc_fpin_delivery_stats_update(struct Scsi_Host *shost,
+                             struct fc_tlv_desc *tlv)
+{
+       struct fc_rport *rport = NULL;
+       struct fc_rport *attach_rport = NULL;
+       struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
+       struct fc_fn_deli_desc *dn_desc = (struct fc_fn_deli_desc *)tlv;
+       u32 reason_code = be32_to_cpu(dn_desc->deli_reason_code);
+
+       rport = fc_find_rport_by_wwpn(shost,
+                                     be64_to_cpu(dn_desc->attached_wwpn));
+       if (rport &&
+           (rport->roles & FC_PORT_ROLE_FCP_TARGET ||
+            rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
+               attach_rport = rport;
+               fc_delivery_stats_update(reason_code,
+                                        &attach_rport->fpin_stats);
+       }
+
+       if (fc_host->port_name == be64_to_cpu(dn_desc->attached_wwpn))
+               fc_delivery_stats_update(reason_code, &fc_host->fpin_stats);
+}
+
+/*
+ * fc_fpin_peer_congn_stats_update - routine to update Peer Congestion
+ * event statistics.
+ * @shost:             host the FPIN was received on
+ * @tlv:               pointer to peer congestion descriptor
+ *
+ */
+static void
+fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost,
+                               struct fc_tlv_desc *tlv)
+{
+       u8 i;
+       struct fc_rport *rport = NULL;
+       struct fc_rport *attach_rport = NULL;
+       struct fc_fn_peer_congn_desc *pc_desc =
+           (struct fc_fn_peer_congn_desc *)tlv;
+       u16 event_type = be16_to_cpu(pc_desc->event_type);
+       u64 wwpn;
+
+       rport = fc_find_rport_by_wwpn(shost,
+                                     be64_to_cpu(pc_desc->attached_wwpn));
+       if (rport &&
+           (rport->roles & FC_PORT_ROLE_FCP_TARGET ||
+            rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
+               attach_rport = rport;
+               fc_cn_stats_update(event_type, &attach_rport->fpin_stats);
+       }
+
+       if (be32_to_cpu(pc_desc->pname_count) > 0) {
+               for (i = 0;
+                   i < be32_to_cpu(pc_desc->pname_count);
+                   i++) {
+                       wwpn = be64_to_cpu(pc_desc->pname_list[i]);
+                       rport = fc_find_rport_by_wwpn(shost, wwpn);
+                       if (rport &&
+                           (rport->roles & FC_PORT_ROLE_FCP_TARGET ||
+                            rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
+                               if (rport == attach_rport)
+                                       continue;
+                               fc_cn_stats_update(event_type,
+                                                  &rport->fpin_stats);
+                       }
+               }
+       }
+}
+
+/*
+ * fc_fpin_congn_stats_update - routine to update Congestion
+ * event statistics.
+ * @shost:             host the FPIN was received on
+ * @tlv:               pointer to congestion descriptor
+ *
+ */
+static void
+fc_fpin_congn_stats_update(struct Scsi_Host *shost,
+                          struct fc_tlv_desc *tlv)
+{
+       struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
+       struct fc_fn_congn_desc *congn = (struct fc_fn_congn_desc *)tlv;
+
+       fc_cn_stats_update(be16_to_cpu(congn->event_type),
+                          &fc_host->fpin_stats);
+}
+
+/**
  * fc_host_rcv_fpin - routine to process a received FPIN.
  * @shost:             host the FPIN was received on
  * @fpin_len:          length of FPIN payload, in bytes
@@ -642,6 +903,38 @@ EXPORT_SYMBOL(fc_host_post_vendor_event);
 void
 fc_host_fpin_rcv(struct Scsi_Host *shost, u32 fpin_len, char *fpin_buf)
 {
+       struct fc_els_fpin *fpin = (struct fc_els_fpin *)fpin_buf;
+       struct fc_tlv_desc *tlv;
+       u32 desc_cnt = 0, bytes_remain;
+       u32 dtag;
+
+       /* Update Statistics */
+       tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0];
+       bytes_remain = fpin_len - offsetof(struct fc_els_fpin, fpin_desc);
+       bytes_remain = min_t(u32, bytes_remain, be32_to_cpu(fpin->desc_len));
+
+       while (bytes_remain >= FC_TLV_DESC_HDR_SZ &&
+              bytes_remain >= FC_TLV_DESC_SZ_FROM_LENGTH(tlv)) {
+               dtag = be32_to_cpu(tlv->desc_tag);
+               switch (dtag) {
+               case ELS_DTAG_LNK_INTEGRITY:
+                       fc_fpin_li_stats_update(shost, tlv);
+                       break;
+               case ELS_DTAG_DELIVERY:
+                       fc_fpin_delivery_stats_update(shost, tlv);
+                       break;
+               case ELS_DTAG_PEER_CONGEST:
+                       fc_fpin_peer_congn_stats_update(shost, tlv);
+                       break;
+               case ELS_DTAG_CONGESTION:
+                       fc_fpin_congn_stats_update(shost, tlv);
+               }
+
+               desc_cnt++;
+               bytes_remain -= FC_TLV_DESC_SZ_FROM_LENGTH(tlv);
+               tlv = fc_tlv_next_desc(tlv);
+       }
+
        fc_host_post_fc_event(shost, fc_get_event_number(),
                                FCH_EVT_LINK_FPIN, fpin_len, fpin_buf, 0);
 }
index 487a403..a636c19 100644 (file)
@@ -819,6 +819,7 @@ void fc_host_post_event(struct Scsi_Host *shost, u32 event_number,
                enum fc_host_event_code event_code, u32 event_data);
 void fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
                u32 data_len, char *data_buf, u64 vendor_id);
+struct fc_rport *fc_find_rport_by_wwpn(struct Scsi_Host *shost, u64 wwpn);
 void fc_host_post_fc_event(struct Scsi_Host *shost, u32 event_number,
                enum fc_host_event_code event_code,
                u32 data_len, char *data_buf, u64 vendor_id);