scsi: fc: Parse FPIN packets and update statistics

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>
This commit is contained in:
Shyam Sundar 2020-10-21 02:27:13 -07:00 committed by Martin K. Petersen
parent 547aab51a9
commit 3dcfe0de5a
2 changed files with 294 additions and 0 deletions

View 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
@ -630,6 +635,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
@ -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);
}

View 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);