diff mbox series

[2/3] qla2xxx: SAN congestion management(SCM) implementation.

Message ID 20200514101026.10040-3-njavali@marvell.com (mailing list archive)
State Changes Requested
Headers show
Series qla2xxx SAN Congestion Management (SCM) support | expand

Commit Message

Nilesh Javali May 14, 2020, 10:10 a.m. UTC
From: Shyam Sundar <ssundar@marvell.com>

* Firmware Initialization with SCM enabled based on NVRAM setting and
  firmware support (About Firmware).
* Enable PUREX and add support for fabric performance impact
  notification(FPIN) handling.
* Support for the following FPIN descriptors:
  	1. Link Integrity Notification.
	2. Delivery Notification.
	3. Peer Congestion Notification.
	4. Congestion Notification.
* Mark a device as slow when a Peer Congestion Notification is received.
* Allocate a default purex item for each vha, to handle memory
  allocation failures in ISR.

Signed-off-by: Shyam Sundar <ssundar@marvell.com>
Signed-off-by: Arun Easi <aeasi@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
---
 drivers/scsi/qla2xxx/qla_bsg.h  | 115 +++++++
 drivers/scsi/qla2xxx/qla_dbg.c  |  13 +-
 drivers/scsi/qla2xxx/qla_def.h  |  89 ++++++
 drivers/scsi/qla2xxx/qla_fw.h   |   7 +-
 drivers/scsi/qla2xxx/qla_gbl.h  |   1 +
 drivers/scsi/qla2xxx/qla_init.c |   9 +-
 drivers/scsi/qla2xxx/qla_isr.c  | 532 ++++++++++++++++++++++++++++++--
 drivers/scsi/qla2xxx/qla_mbx.c  |  45 ++-
 drivers/scsi/qla2xxx/qla_os.c   |  18 ++
 9 files changed, 795 insertions(+), 34 deletions(-)

Comments

Himanshu Madhani May 14, 2020, 6:52 p.m. UTC | #1
Nilesh,

On 5/14/20 5:10 AM, Nilesh Javali wrote:
> * Firmware Initialization with SCM enabled based on NVRAM setting and
>    firmware support (About Firmware).
> * Enable PUREX and add support for fabric performance impact
>    notification(FPIN) handling.
> * Support for the following FPIN descriptors:
>    	1. Link Integrity Notification.
> 	2. Delivery Notification.
> 	3. Peer Congestion Notification.
> 	4. Congestion Notification.
> * Mark a device as slow when a Peer Congestion Notification is received.
> * Allocate a default purex item for each vha, to handle memory
>    allocation failures in ISR.

When you repost this series, fix comments for function header and places 
where its using windows style comments in this patch.
James Smart May 15, 2020, 10:48 p.m. UTC | #2
On 5/14/2020 3:10 AM, Nilesh Javali wrote:
> From: Shyam Sundar <ssundar@marvell.com>
>
> * Firmware Initialization with SCM enabled based on NVRAM setting and
>    firmware support (About Firmware).
> * Enable PUREX and add support for fabric performance impact
>    notification(FPIN) handling.
> * Support for the following FPIN descriptors:
>    	1. Link Integrity Notification.
> 	2. Delivery Notification.
> 	3. Peer Congestion Notification.
> 	4. Congestion Notification.
> * Mark a device as slow when a Peer Congestion Notification is received.
> * Allocate a default purex item for each vha, to handle memory
>    allocation failures in ISR.

In general, there's a lot of generic things here that are done in 
driver-specific manners.

All the FPIN statistics should be added to the scsi fc transport objects 
and transport routines created to parse the fpin payloads and set 
statistics.  Also, statistics can be reported via sysfs on the transport 
object rather than creating vendor-specific bsg requests to obtain them.

In line with this - FPIN definitions should use the existing the 
existing common headers in include/uapi/fc/fc_els.h.  The file doesn't 
have the congestion fpin definitions, so rather than putting in a driver 
header - put the structure definitions in the common header.


>
> Signed-off-by: Shyam Sundar <ssundar@marvell.com>
> Signed-off-by: Arun Easi <aeasi@marvell.com>
> Signed-off-by: Nilesh Javali <njavali@marvell.com>
> ---
>   drivers/scsi/qla2xxx/qla_bsg.h  | 115 +++++++
>   drivers/scsi/qla2xxx/qla_dbg.c  |  13 +-
>   drivers/scsi/qla2xxx/qla_def.h  |  89 ++++++
>   drivers/scsi/qla2xxx/qla_fw.h   |   7 +-
>   drivers/scsi/qla2xxx/qla_gbl.h  |   1 +
>   drivers/scsi/qla2xxx/qla_init.c |   9 +-
>   drivers/scsi/qla2xxx/qla_isr.c  | 532 ++++++++++++++++++++++++++++++--
>   drivers/scsi/qla2xxx/qla_mbx.c  |  45 ++-
>   drivers/scsi/qla2xxx/qla_os.c   |  18 ++
>   9 files changed, 795 insertions(+), 34 deletions(-)
>
> diff --git a/drivers/scsi/qla2xxx/qla_bsg.h b/drivers/scsi/qla2xxx/qla_bsg.h
> index 7594fad7b5b5..0b308859047c 100644
> --- a/drivers/scsi/qla2xxx/qla_bsg.h
> +++ b/drivers/scsi/qla2xxx/qla_bsg.h
> @@ -290,4 +290,119 @@ struct qla_active_regions {
>   	uint8_t reserved[32];
>   } __packed;
>   
> +#define SCM_LINK_EVENT_UNKNOWN			0x0
> +#define SCM_LINK_EVENT_LINK_FAILURE			0x1
> +#define SCM_LINK_EVENT_LOSS_OF_SYNC			0x2
> +#define SCM_LINK_EVENT_LOSS_OF_SIGNAL		0x3
> +#define SCM_LINK_EVENT_PRIMITIVE_SEQ_PROTOCOL_ERROR	0x4
> +#define SCM_LINK_EVENT_INVALID_TX_WORD		0x5
> +#define SCM_LINK_EVENT_INVALID_CRC			0x6
> +#define SCM_LINK_EVENT_DEVICE_SPECIFIC		0xF
> +#define SCM_LINK_EVENT_V1_SIZE			20

These should be the existing defines in the common fc_els.h header. Also 
make sure SCM_LINK_EVENT_V1_SIZE is actually used.

> +struct qla_scm_link_event {
> +	uint64_t	timestamp;
> +	uint16_t	event_type;
> +	uint16_t	event_modifier;
> +	uint32_t	event_threshold;
> +	uint32_t	event_count;
> +	uint8_t		reserved[12];
> +} __packed;
> +
> +#define SCM_DELIVERY_REASON_UNKNOWN		0x0
> +#define SCM_DELIVERY_REASON_TIMEOUT		0x1
> +#define SCM_DELIVERY_REASON_UNABLE_TO_ROUTE	0x2
> +#define SCM_DELIVERY_REASON_DEVICE_SPECIFIC	0xF
> +struct qla_scm_delivery_event {
> +	uint64_t	timestamp;
> +	uint32_t	delivery_reason;
> +	uint8_t		deliver_frame_hdr[24];
> +	uint8_t		reserved[28];
> +
> +} __packed;
> +
> +#define SCM_CONGESTION_EVENT_CLEAR		0x0
> +#define SCM_CONGESTION_EVENT_LOST_CREDIT	0x1
> +#define SCM_CONGESTION_EVENT_CREDIT_STALL	0x2
> +#define SCM_CONGESTION_EVENT_OVERSUBSCRIPTION	0x3
> +#define SCM_CONGESTION_EVENT_DEVICE_SPECIFIC	0xF
> +struct qla_scm_peer_congestion_event {
> +	uint64_t	timestamp;
> +	uint16_t	event_type;
> +	uint16_t	event_modifier;
> +	uint32_t	event_period;
> +	uint8_t		reserved[16];
> +} __packed;
> +
> +#define SCM_CONGESTION_SEVERITY_WARNING	0xF1
> +#define SCM_CONGESTION_SEVERITY_ERROR	0xF7
> +struct qla_scm_congestion_event {
> +	uint64_t	timestamp;
> +	uint16_t	event_type;
> +	uint16_t	event_modifier;
> +	uint32_t	event_period;
> +	uint8_t		severity;
> +	uint8_t		reserved[15];
> +} __packed;

I expect many of these defines also should map to std-defined defines, 
thus should be added to fc_els.h


> +
> +#define SCM_FLAG_RDF_REJECT		0x00
> +#define SCM_FLAG_RDF_COMPLETED		0x01
> +#define SCM_FLAG_BROCADE_CONNECTED	0x02
> +#define SCM_FLAG_CISCO_CONNECTED	0x04
> +
> +#define SCM_STATE_CONGESTION	0x1
> +#define SCM_STATE_DELIVERY		0x2
> +#define SCM_STATE_LINK_INTEGRITY	0x4
> +#define SCM_STATE_PEER_CONGESTION	0x8
> +
> +#define QLA_CON_PRIMITIVE_RECEIVED	0x1
> +#define QLA_CONGESTION_ARB_WARNING	0x1
> +#define QLA_CONGESTION_ARB_ALARM	0X2
> +struct qla_scm_port {
> +	uint32_t			current_events;
> +
> +	struct qla_scm_link_event	link_integrity;
> +	struct qla_scm_delivery_event	delivery;
> +	struct qla_scm_congestion_event	congestion;
> +	uint64_t			scm_congestion_alarm;
> +	uint64_t			scm_congestion_warning;
> +	uint8_t				scm_fabric_connection_flags;
> +	uint8_t				reserved[43];
> +} __packed;
> +
> +struct qla_scm_target {
> +	uint8_t		wwpn[8];
> +	uint32_t	current_events;
> +
> +	struct qla_scm_link_event		link_integrity;
> +	struct qla_scm_delivery_event		delivery;
> +	struct qla_scm_peer_congestion_event	peer_congestion;
> +
> +	uint32_t	link_failure_count;
> +	uint32_t	loss_of_sync_count;
> +	uint32_t        loss_of_signals_count;
> +	uint32_t        primitive_seq_protocol_err_count;
> +	uint32_t        invalid_transmission_word_count;
> +	uint32_t        invalid_crc_count;
> +
> +	uint32_t        delivery_failure_unknown;
> +	uint32_t        delivery_timeout;
> +	uint32_t        delivery_unable_to_route;
> +	uint32_t        delivery_failure_device_specific;
> +
> +	uint32_t        peer_congestion_clear;
> +	uint32_t        peer_congestion_lost_credit;
> +	uint32_t        peer_congestion_credit_stall;
> +	uint32_t        peer_congestion_oversubscription;
> +	uint32_t        peer_congestion_device_specific;
> +	uint32_t	link_unknown_event;
> +	uint32_t	link_device_specific_event;
> +	uint8_t		reserved[48];
> +} __packed;
> +

Q: what purpose are these shorter "meta" event structures serving ? Why 
hold onto (what I assume is) the last event.  Wouldn't something 
monitoring netlink and use of the existing fc_host_fpin_rcv() interface 
be enough ? it should see all events.


>   
> +#define SCM_EDC_ACC_RECEIVED		BIT_6
> +#define SCM_RDF_ACC_RECEIVED		BIT_7
> +#define SCM_NOTIFICATION_TYPE_LINK_INTEGRITY	0x00020001
> +#define SCM_NOTIFICATION_TYPE_DELIVERY		0x00020002
> +#define SCM_NOTIFICATION_TYPE_PEER_CONGESTION	0x00020003
> +#define SCM_NOTIFICATION_TYPE_CONGESTION	0x00020004
> +#define FPIN_DESCRIPTOR_HEADER_SIZE	4
> +#define FPIN_ELS_DESCRIPTOR_LIST_OFFSET	8
> +struct fpin_descriptor {
> +	__be32 descriptor_tag;
> +	__be32 descriptor_length;
> +	union {
> +		uint8_t common_detecting_port_name[WWN_SIZE];
> +		struct {
> +			uint8_t detecting_port_name[WWN_SIZE];
> +			uint8_t attached_port_name[WWN_SIZE];
> +			__be16 event_type;
> +			__be16 event_modifier;
> +			__be32 event_threshold;
> +			__be32 event_count;
> +			__be32 port_name_count;
> +			uint8_t port_name_list[1][WWN_SIZE];
> +		} link_integrity;
> +		struct {
> +			uint8_t detecting_port_name[WWN_SIZE];
> +			uint8_t attached_port_name[WWN_SIZE];
> +			__be32 delivery_reason_code;
> +		} delivery;
> +		struct {
> +			uint8_t detecting_port_name[WWN_SIZE];
> +			uint8_t attached_port_name[WWN_SIZE];
> +			__be16 event_type;
> +			__be16 event_modifier;
> +			__be32 event_period;
> +			__be32 port_name_count;
> +			uint8_t port_name_list[1][WWN_SIZE];
> +		} peer_congestion;
> +		struct {
> +			__be16 event_type;
> +			__be16 event_modifier;
> +			__be32 event_period;
> +			uint8_t severity;
> +			uint8_t reserved[3];
> +		} congestion;
> +	};
> +};

The fpin descriptor is already in the common fc_els.h header. Use it.  
And feel free to extend the common header definitions for the 
congestion/delivery events.


>   
> +void
> +qla_link_integrity_tgt_stats_update(struct fpin_descriptor *fpin_desc,
> +				    fc_port_t *fcport)
> +{
> +	ql_log(ql_log_info, fcport->vha, 0x502d,
> +	       "Link Integrity Event Type %d for Port %8phN\n",
> +	       be16_to_cpu(fpin_desc->link_integrity.event_type),
> +	       fcport->port_name);
> +
> +	fcport->scm_stats.link_integrity.event_type =
> +	    be16_to_cpu(fpin_desc->link_integrity.event_type);
> +	fcport->scm_stats.link_integrity.event_modifier =
> +	    be16_to_cpu(fpin_desc->link_integrity.event_modifier);
> +	fcport->scm_stats.link_integrity.event_threshold =
> +	    be32_to_cpu(fpin_desc->link_integrity.event_threshold);
> +	fcport->scm_stats.link_integrity.event_count =
> +	    be32_to_cpu(fpin_desc->link_integrity.event_count);
> +	fcport->scm_stats.link_integrity.timestamp = ktime_get_real_seconds();
> +
> +	fcport->scm_stats.current_events |= SCM_STATE_LINK_INTEGRITY;
> +	switch (be16_to_cpu(fpin_desc->link_integrity.event_type)) {
> +	case SCM_LINK_EVENT_UNKNOWN:
> +		fcport->scm_stats.link_unknown_event +=
> +		    be32_to_cpu(fpin_desc->link_integrity.event_count);
> +		break;
> +	case SCM_LINK_EVENT_LINK_FAILURE:
> +		fcport->scm_stats.link_failure_count +=
> +		    be32_to_cpu(fpin_desc->link_integrity.event_count);
> +		break;
> +	case SCM_LINK_EVENT_LOSS_OF_SYNC:
> +		fcport->scm_stats.loss_of_sync_count +=
> +		    be32_to_cpu(fpin_desc->link_integrity.event_count);
> +		break;
> +	case SCM_LINK_EVENT_LOSS_OF_SIGNAL:
> +		fcport->scm_stats.loss_of_signals_count +=
> +		    be32_to_cpu(fpin_desc->link_integrity.event_count);
> +		break;
> +	case SCM_LINK_EVENT_PRIMITIVE_SEQ_PROTOCOL_ERROR:
> +		fcport->scm_stats.primitive_seq_protocol_err_count +=
> +		    be32_to_cpu(fpin_desc->link_integrity.event_count);
> +		break;
> +	case SCM_LINK_EVENT_INVALID_TX_WORD:
> +		fcport->scm_stats.invalid_transmission_word_count +=
> +		    be32_to_cpu(fpin_desc->link_integrity.event_count);
> +		break;
> +	case SCM_LINK_EVENT_INVALID_CRC:
> +		fcport->scm_stats.invalid_crc_count +=
> +		    be32_to_cpu(fpin_desc->link_integrity.event_count);
> +		break;
> +	case SCM_LINK_EVENT_DEVICE_SPECIFIC:
> +		fcport->scm_stats.link_device_specific_event +=
> +		    be32_to_cpu(fpin_desc->link_integrity.event_count);
> +		break;
> +	}
> +}
> +

A prime example of a routine that should be put into the fc transport 
and a list of statistics that should be visible via sysfs on the 
transport host (aka port) object.


> +void
> +qla_scm_process_link_integrity_d(struct scsi_qla_host *vha,
> +				 struct fpin_descriptor *fpin_desc)
> +{
> ...
> +}
> +
> +void
> +qla_delivery_tgt_stats_update(struct fpin_descriptor *fpin_desc,
> +			      fc_port_t *fcport)
> +{
> ...
> +}
> +
> +/*
> + * Process Delivery Notification Descriptor
> + */
> +void
> +qla_scm_process_delivery_notification_d(struct scsi_qla_host *vha,
> +					struct fpin_descriptor *fpin_desc)
> +{
> ...
> +}
> +
> ...
> +
> +void
> +qla_peer_congestion_tgt_stats_update(struct fpin_descriptor *fpin_desc,
> +				     fc_port_t *fcport)
> +{
> ...
> +}
> +
> +/*
> + * Process Peer-Congestion Notification Descriptor
> + */
> +void
> +qla_scm_process_peer_congestion_notification_d(struct scsi_qla_host *vha,
> +					struct fpin_descriptor *fpin_desc)
> +{
> ...
> +}
> +
> +/*
> + * qla_scm_process_congestion_notification_d() - Process
> + * Process Congestion Notification Descriptor
> + * @rsp: response queue
> + * @pkt: Entry pointer
> + */
> +void
> +qla_scm_process_congestion_notification_d(struct scsi_qla_host *vha,
> +					  struct fpin_descriptor *fpin_desc)
> ...
> +}

Same comment as prior - should be in scsi fc transport routines and 
stats set on appropriate transport object

-- james
Shyam Sundar June 11, 2020, 5:42 p.m. UTC | #3
Seems like this (and a previous email) never made it to the reflector, resending.

The suggestions make sense to me, and I have made most of the recommended changes.
I was looking for some guidance on placements of the sim stats structures.

> On May 29, 2020, at 11:53 AM, Shyam Sundar <ssundar@marvell.com> wrote:
> 
> James,
>      I was thinking of adding a structures for tracking the target FPIN stats.
> 
> struct fc_rport_statistics {
> uint32_t link_failure_count;
> uint32_t loss_of_sync_count;
> ....
> }
> 
> under fc_rport:
> 
> struct fc_rport {
> 
> /* Private (Transport-managed) Attributes */
> struct fc_rport_statistics;
> 
>      For host FPIN stats (essentially the alarm & warning), was not sure if I should add them to the fc_host_statistics or
> define a new structure under the Private Attributes section within the fc_host_attrs/fc_vport.
> 
>      In theory, given that the host stats could be updated both via signals and FPIN, one could argue that it would make sense
> to maintain it with the current host statistics, but keeping it confined to transport will ensure we have a uniform way of handling
> congestion and peer congestion events.  
> 
>     Would appreciate your thoughts there.
> 
> Thanks
> Shyam
>    
> ---------- Forwarded message ---------
> From: James Smart <james.smart@broadcom.com>
> Date: Fri, May 15, 2020 at 3:51 PM
> Subject: Re: [PATCH 2/3] qla2xxx: SAN congestion management(SCM) implementation.
> To: Nilesh Javali <njavali@marvell.com>, <martin.petersen@oracle.com>
> Cc: <linux-scsi@vger.kernel.org>, <GR-QLogic-Storage-Upstream@marvell.com>
> 
> 
> 
> 
> On 5/14/2020 3:10 AM, Nilesh Javali wrote:
> > From: Shyam Sundar <ssundar@marvell.com>
> >
> > * Firmware Initialization with SCM enabled based on NVRAM setting and
> >    firmware support (About Firmware).
> > * Enable PUREX and add support for fabric performance impact
> >    notification(FPIN) handling.
> > * Support for the following FPIN descriptors:
> >       1. Link Integrity Notification.
> >       2. Delivery Notification.
> >       3. Peer Congestion Notification.
> >       4. Congestion Notification.
> > * Mark a device as slow when a Peer Congestion Notification is received.
> > * Allocate a default purex item for each vha, to handle memory
> >    allocation failures in ISR.
> 
> In general, there's a lot of generic things here that are done in 
> driver-specific manners.
> 
> All the FPIN statistics should be added to the scsi fc transport objects 
> and transport routines created to parse the fpin payloads and set 
> statistics.  Also, statistics can be reported via sysfs on the transport 
> object rather than creating vendor-specific bsg requests to obtain them.
> 
> In line with this - FPIN definitions should use the existing the 
> existing common headers in include/uapi/fc/fc_els.h.  The file doesn't 
> have the congestion fpin definitions, so rather than putting in a driver 
> header - put the structure definitions in the common header.
> 
> 
> >
> > Signed-off-by: Shyam Sundar <ssundar@marvell.com>
> > Signed-off-by: Arun Easi <aeasi@marvell.com>
> > Signed-off-by: Nilesh Javali <njavali@marvell.com>
> > ---
> >   drivers/scsi/qla2xxx/qla_bsg.h  | 115 +++++++
> >   drivers/scsi/qla2xxx/qla_dbg.c  |  13 +-
> >   drivers/scsi/qla2xxx/qla_def.h  |  89 ++++++
> >   drivers/scsi/qla2xxx/qla_fw.h   |   7 +-
> >   drivers/scsi/qla2xxx/qla_gbl.h  |   1 +
> >   drivers/scsi/qla2xxx/qla_init.c |   9 +-
> >   drivers/scsi/qla2xxx/qla_isr.c  | 532 ++++++++++++++++++++++++++++++--
> >   drivers/scsi/qla2xxx/qla_mbx.c  |  45 ++-
> >   drivers/scsi/qla2xxx/qla_os.c   |  18 ++
> >   9 files changed, 795 insertions(+), 34 deletions(-)
> >
> > diff --git a/drivers/scsi/qla2xxx/qla_bsg.h b/drivers/scsi/qla2xxx/qla_bsg.h
> > index 7594fad7b5b5..0b308859047c 100644
> > --- a/drivers/scsi/qla2xxx/qla_bsg.h
> > +++ b/drivers/scsi/qla2xxx/qla_bsg.h
> > @@ -290,4 +290,119 @@ struct qla_active_regions {
> >       uint8_t reserved[32];
> >   } __packed;
> >   
> > +#define SCM_LINK_EVENT_UNKNOWN                       0x0
> > +#define SCM_LINK_EVENT_LINK_FAILURE                  0x1
> > +#define SCM_LINK_EVENT_LOSS_OF_SYNC                  0x2
> > +#define SCM_LINK_EVENT_LOSS_OF_SIGNAL                0x3
> > +#define SCM_LINK_EVENT_PRIMITIVE_SEQ_PROTOCOL_ERROR  0x4
> > +#define SCM_LINK_EVENT_INVALID_TX_WORD               0x5
> > +#define SCM_LINK_EVENT_INVALID_CRC                   0x6
> > +#define SCM_LINK_EVENT_DEVICE_SPECIFIC               0xF
> > +#define SCM_LINK_EVENT_V1_SIZE                       20
> 
> These should be the existing defines in the common fc_els.h header. Also 
> make sure SCM_LINK_EVENT_V1_SIZE is actually used.
> 
> > +struct qla_scm_link_event {
> > +     uint64_t        timestamp;
> > +     uint16_t        event_type;
> > +     uint16_t        event_modifier;
> > +     uint32_t        event_threshold;
> > +     uint32_t        event_count;
> > +     uint8_t         reserved[12];
> > +} __packed;
> > +
> > +#define SCM_DELIVERY_REASON_UNKNOWN          0x0
> > +#define SCM_DELIVERY_REASON_TIMEOUT          0x1
> > +#define SCM_DELIVERY_REASON_UNABLE_TO_ROUTE  0x2
> > +#define SCM_DELIVERY_REASON_DEVICE_SPECIFIC  0xF
> > +struct qla_scm_delivery_event {
> > +     uint64_t        timestamp;
> > +     uint32_t        delivery_reason;
> > +     uint8_t         deliver_frame_hdr[24];
> > +     uint8_t         reserved[28];
> > +
> > +} __packed;
> > +
> > +#define SCM_CONGESTION_EVENT_CLEAR           0x0
> > +#define SCM_CONGESTION_EVENT_LOST_CREDIT     0x1
> > +#define SCM_CONGESTION_EVENT_CREDIT_STALL    0x2
> > +#define SCM_CONGESTION_EVENT_OVERSUBSCRIPTION        0x3
> > +#define SCM_CONGESTION_EVENT_DEVICE_SPECIFIC 0xF
> > +struct qla_scm_peer_congestion_event {
> > +     uint64_t        timestamp;
> > +     uint16_t        event_type;
> > +     uint16_t        event_modifier;
> > +     uint32_t        event_period;
> > +     uint8_t         reserved[16];
> > +} __packed;
> > +
> > +#define SCM_CONGESTION_SEVERITY_WARNING      0xF1
> > +#define SCM_CONGESTION_SEVERITY_ERROR        0xF7
> > +struct qla_scm_congestion_event {
> > +     uint64_t        timestamp;
> > +     uint16_t        event_type;
> > +     uint16_t        event_modifier;
> > +     uint32_t        event_period;
> > +     uint8_t         severity;
> > +     uint8_t         reserved[15];
> > +} __packed;
> 
> I expect many of these defines also should map to std-defined defines, 
> thus should be added to fc_els.h
> 
> 
> > +
> > +#define SCM_FLAG_RDF_REJECT          0x00
> > +#define SCM_FLAG_RDF_COMPLETED               0x01
> > +#define SCM_FLAG_BROCADE_CONNECTED   0x02
> > +#define SCM_FLAG_CISCO_CONNECTED     0x04
> > +
> > +#define SCM_STATE_CONGESTION 0x1
> > +#define SCM_STATE_DELIVERY           0x2
> > +#define SCM_STATE_LINK_INTEGRITY     0x4
> > +#define SCM_STATE_PEER_CONGESTION    0x8
> > +
> > +#define QLA_CON_PRIMITIVE_RECEIVED   0x1
> > +#define QLA_CONGESTION_ARB_WARNING   0x1
> > +#define QLA_CONGESTION_ARB_ALARM     0X2
> > +struct qla_scm_port {
> > +     uint32_t                        current_events;
> > +
> > +     struct qla_scm_link_event       link_integrity;
> > +     struct qla_scm_delivery_event   delivery;
> > +     struct qla_scm_congestion_event congestion;
> > +     uint64_t                        scm_congestion_alarm;
> > +     uint64_t                        scm_congestion_warning;
> > +     uint8_t                         scm_fabric_connection_flags;
> > +     uint8_t                         reserved[43];
> > +} __packed;
> > +
> > +struct qla_scm_target {
> > +     uint8_t         wwpn[8];
> > +     uint32_t        current_events;
> > +
> > +     struct qla_scm_link_event               link_integrity;
> > +     struct qla_scm_delivery_event           delivery;
> > +     struct qla_scm_peer_congestion_event    peer_congestion;
> > +
> > +     uint32_t        link_failure_count;
> > +     uint32_t        loss_of_sync_count;
> > +     uint32_t        loss_of_signals_count;
> > +     uint32_t        primitive_seq_protocol_err_count;
> > +     uint32_t        invalid_transmission_word_count;
> > +     uint32_t        invalid_crc_count;
> > +
> > +     uint32_t        delivery_failure_unknown;
> > +     uint32_t        delivery_timeout;
> > +     uint32_t        delivery_unable_to_route;
> > +     uint32_t        delivery_failure_device_specific;
> > +
> > +     uint32_t        peer_congestion_clear;
> > +     uint32_t        peer_congestion_lost_credit;
> > +     uint32_t        peer_congestion_credit_stall;
> > +     uint32_t        peer_congestion_oversubscription;
> > +     uint32_t        peer_congestion_device_specific;
> > +     uint32_t        link_unknown_event;
> > +     uint32_t        link_device_specific_event;
> > +     uint8_t         reserved[48];
> > +} __packed;
> > +
> 
> Q: what purpose are these shorter "meta" event structures serving ? Why 
> hold onto (what I assume is) the last event.  Wouldn't something 
> monitoring netlink and use of the existing fc_host_fpin_rcv() interface 
> be enough ? it should see all events.
> 
> 
> >   
> > +#define SCM_EDC_ACC_RECEIVED         BIT_6
> > +#define SCM_RDF_ACC_RECEIVED         BIT_7
> > +#define SCM_NOTIFICATION_TYPE_LINK_INTEGRITY 0x00020001
> > +#define SCM_NOTIFICATION_TYPE_DELIVERY               0x00020002
> > +#define SCM_NOTIFICATION_TYPE_PEER_CONGESTION        0x00020003
> > +#define SCM_NOTIFICATION_TYPE_CONGESTION     0x00020004
> > +#define FPIN_DESCRIPTOR_HEADER_SIZE  4
> > +#define FPIN_ELS_DESCRIPTOR_LIST_OFFSET      8
> > +struct fpin_descriptor {
> > +     __be32 descriptor_tag;
> > +     __be32 descriptor_length;
> > +     union {
> > +             uint8_t common_detecting_port_name[WWN_SIZE];
> > +             struct {
> > +                     uint8_t detecting_port_name[WWN_SIZE];
> > +                     uint8_t attached_port_name[WWN_SIZE];
> > +                     __be16 event_type;
> > +                     __be16 event_modifier;
> > +                     __be32 event_threshold;
> > +                     __be32 event_count;
> > +                     __be32 port_name_count;
> > +                     uint8_t port_name_list[1][WWN_SIZE];
> > +             } link_integrity;
> > +             struct {
> > +                     uint8_t detecting_port_name[WWN_SIZE];
> > +                     uint8_t attached_port_name[WWN_SIZE];
> > +                     __be32 delivery_reason_code;
> > +             } delivery;
> > +             struct {
> > +                     uint8_t detecting_port_name[WWN_SIZE];
> > +                     uint8_t attached_port_name[WWN_SIZE];
> > +                     __be16 event_type;
> > +                     __be16 event_modifier;
> > +                     __be32 event_period;
> > +                     __be32 port_name_count;
> > +                     uint8_t port_name_list[1][WWN_SIZE];
> > +             } peer_congestion;
> > +             struct {
> > +                     __be16 event_type;
> > +                     __be16 event_modifier;
> > +                     __be32 event_period;
> > +                     uint8_t severity;
> > +                     uint8_t reserved[3];
> > +             } congestion;
> > +     };
> > +};
> 
> The fpin descriptor is already in the common fc_els.h header. Use it.  
> And feel free to extend the common header definitions for the 
> congestion/delivery events.
> 
> 
> >   
> > +void
> > +qla_link_integrity_tgt_stats_update(struct fpin_descriptor *fpin_desc,
> > +                                 fc_port_t *fcport)
> > +{
> > +     ql_log(ql_log_info, fcport->vha, 0x502d,
> > +            "Link Integrity Event Type %d for Port %8phN\n",
> > +            be16_to_cpu(fpin_desc->link_integrity.event_type),
> > +            fcport->port_name);
> > +
> > +     fcport->scm_stats.link_integrity.event_type =
> > +         be16_to_cpu(fpin_desc->link_integrity.event_type);
> > +     fcport->scm_stats.link_integrity.event_modifier =
> > +         be16_to_cpu(fpin_desc->link_integrity.event_modifier);
> > +     fcport->scm_stats.link_integrity.event_threshold =
> > +         be32_to_cpu(fpin_desc->link_integrity.event_threshold);
> > +     fcport->scm_stats.link_integrity.event_count =
> > +         be32_to_cpu(fpin_desc->link_integrity.event_count);
> > +     fcport->scm_stats.link_integrity.timestamp = ktime_get_real_seconds();
> > +
> > +     fcport->scm_stats.current_events |= SCM_STATE_LINK_INTEGRITY;
> > +     switch (be16_to_cpu(fpin_desc->link_integrity.event_type)) {
> > +     case SCM_LINK_EVENT_UNKNOWN:
> > +             fcport->scm_stats.link_unknown_event +=
> > +                 be32_to_cpu(fpin_desc->link_integrity.event_count);
> > +             break;
> > +     case SCM_LINK_EVENT_LINK_FAILURE:
> > +             fcport->scm_stats.link_failure_count +=
> > +                 be32_to_cpu(fpin_desc->link_integrity.event_count);
> > +             break;
> > +     case SCM_LINK_EVENT_LOSS_OF_SYNC:
> > +             fcport->scm_stats.loss_of_sync_count +=
> > +                 be32_to_cpu(fpin_desc->link_integrity.event_count);
> > +             break;
> > +     case SCM_LINK_EVENT_LOSS_OF_SIGNAL:
> > +             fcport->scm_stats.loss_of_signals_count +=
> > +                 be32_to_cpu(fpin_desc->link_integrity.event_count);
> > +             break;
> > +     case SCM_LINK_EVENT_PRIMITIVE_SEQ_PROTOCOL_ERROR:
> > +             fcport->scm_stats.primitive_seq_protocol_err_count +=
> > +                 be32_to_cpu(fpin_desc->link_integrity.event_count);
> > +             break;
> > +     case SCM_LINK_EVENT_INVALID_TX_WORD:
> > +             fcport->scm_stats.invalid_transmission_word_count +=
> > +                 be32_to_cpu(fpin_desc->link_integrity.event_count);
> > +             break;
> > +     case SCM_LINK_EVENT_INVALID_CRC:
> > +             fcport->scm_stats.invalid_crc_count +=
> > +                 be32_to_cpu(fpin_desc->link_integrity.event_count);
> > +             break;
> > +     case SCM_LINK_EVENT_DEVICE_SPECIFIC:
> > +             fcport->scm_stats.link_device_specific_event +=
> > +                 be32_to_cpu(fpin_desc->link_integrity.event_count);
> > +             break;
> > +     }
> > +}
> > +
> 
> A prime example of a routine that should be put into the fc transport 
> and a list of statistics that should be visible via sysfs on the 
> transport host (aka port) object.
> 
> 
> > +void
> > +qla_scm_process_link_integrity_d(struct scsi_qla_host *vha,
> > +                              struct fpin_descriptor *fpin_desc)
> > +{
> > ...
> > +}
> > +
> > +void
> > +qla_delivery_tgt_stats_update(struct fpin_descriptor *fpin_desc,
> > +                           fc_port_t *fcport)
> > +{
> > ...
> > +}
> > +
> > +/*
> > + * Process Delivery Notification Descriptor
> > + */
> > +void
> > +qla_scm_process_delivery_notification_d(struct scsi_qla_host *vha,
> > +                                     struct fpin_descriptor *fpin_desc)
> > +{
> > ...
> > +}
> > +
> > ...
> > +
> > +void
> > +qla_peer_congestion_tgt_stats_update(struct fpin_descriptor *fpin_desc,
> > +                                  fc_port_t *fcport)
> > +{
> > ...
> > +}
> > +
> > +/*
> > + * Process Peer-Congestion Notification Descriptor
> > + */
> > +void
> > +qla_scm_process_peer_congestion_notification_d(struct scsi_qla_host *vha,
> > +                                     struct fpin_descriptor *fpin_desc)
> > +{
> > ...
> > +}
> > +
> > +/*
> > + * qla_scm_process_congestion_notification_d() - Process
> > + * Process Congestion Notification Descriptor
> > + * @rsp: response queue
> > + * @pkt: Entry pointer
> > + */
> > +void
> > +qla_scm_process_congestion_notification_d(struct scsi_qla_host *vha,
> > +                                       struct fpin_descriptor *fpin_desc)
> > ...
> > +}
> 
> Same comment as prior - should be in scsi fc transport routines and 
> stats set on appropriate transport object
> 
> -- james
Shyam S June 11, 2020, 6:10 p.m. UTC | #4
Resending to the reflector.

On Mon, May 18, 2020 at 4:25 PM Shyam S <born27thfeb@gmail.com> wrote:
>
>
>>
>>
>> > Q: what purpose are these shorter "meta" event structures serving ? Why
>> > hold onto (what I assume is) the last event.  Wouldn't something
>> > monitoring netlink and use of the existing fc_host_fpin_rcv() interface
>> > be enough ? it should see all events.
>
>
> The Idea was to be able to have a longer term view within the driver so that while debugging issues, correlations can be made between symptoms exhibited by the driver with these statistics.
>
> All your other comments make complete sense to me. I'll re-work the changes to move them up the stack.
>
>
> Regards
> Shyam
James Smart June 25, 2020, 11:25 p.m. UTC | #5
On 6/11/2020 10:42 AM, Shyam Sundar wrote:
> Seems like this (and a previous email) never made it to the reflector, resending.
>
> The suggestions make sense to me, and I have made most of the recommended changes.
> I was looking for some guidance on placements of the sim stats structures.
>
>> On May 29, 2020, at 11:53 AM, Shyam Sundar <ssundar@marvell.com> wrote:
>>
>> James,
>>       I was thinking of adding a structures for tracking the target FPIN stats.
>>
>> struct fc_rport_statistics {
>> uint32_t link_failure_count;
>> uint32_t loss_of_sync_count;
>> ....
>> }
>>
>> under fc_rport:
>>
>> struct fc_rport {
>>
>> /* Private (Transport-managed) Attributes */
>> struct fc_rport_statistics;
>>
>>       For host FPIN stats (essentially the alarm & warning), was not sure if I should add them to the fc_host_statistics or
>> define a new structure under the Private Attributes section within the fc_host_attrs/fc_vport.

my initial thought was the same

>>
>>       In theory, given that the host stats could be updated both via signals and FPIN, one could argue that it would make sense
>> to maintain it with the current host statistics, but keeping it confined to transport will ensure we have a uniform way of handling
>> congestion and peer congestion events.

but then I have the same thoughts as well. And the commonization of the 
parsing and incrementing makes a lot more sense.

>>
>>      Would appreciate your thoughts there.
>>
>> Thanks
>> Shyam
>>     
>>


I would put them under the Dynamic attributes area in fc_host_attrs and 
fc_rport.
fc_host_attrs:
fpin_cn              incremented for each Congestion Notify FPIN
cn_sig_warn     incremented for each congestion warning signal
cn_sig_alarm    incremented for each congestion alarm signal
fpin_dn             incremented for each Delivery Notification FPIN 
where attached wwpn is local port
fpin_li                incremented for each Link Integrity FPIN where 
attached wwpn is local port

fc_rport:
fpin_dn             incremented for each Delivery Notification FPIN 
where attached wwpn is the rport
fpin_li               incremented for each Link Integrity FPIN where 
attached wwpn is the rport
fpin_pcn           incremented for each Peer Congestion Notify FPIN 
where attached wwpn is the rport

For the cn_sig_xxx values, the driver would just increment them.
For the fpin_xxx values - we'll augment the fc_host_fpin_rcv routine to 
parse the fpin and increment the fc_host or fc_rport counter.

-- james
Shyam Sundar June 26, 2020, 12:14 a.m. UTC | #6
> On Jun 25, 2020, at 4:25 PM, James Smart <james.smart@broadcom.com> wrote:
> 
> 
> 
> On 6/11/2020 10:42 AM, Shyam Sundar wrote:
>> Seems like this (and a previous email) never made it to the reflector, resending.
>> 
>> The suggestions make sense to me, and I have made most of the recommended changes.
>> I was looking for some guidance on placements of the sim stats structures.
>> 
>>> On May 29, 2020, at 11:53 AM, Shyam Sundar <ssundar@marvell.com> wrote:
>>> 
>>> James,
>>>      I was thinking of adding a structures for tracking the target FPIN stats.
>>> 
>>> struct fc_rport_statistics {
>>> uint32_t link_failure_count;
>>> uint32_t loss_of_sync_count;
>>> ....
>>> }
>>> 
>>> under fc_rport:
>>> 
>>> struct fc_rport {
>>> 
>>> /* Private (Transport-managed) Attributes */
>>> struct fc_rport_statistics;
>>> 
>>>      For host FPIN stats (essentially the alarm & warning), was not sure if I should add them to the fc_host_statistics or
>>> define a new structure under the Private Attributes section within the fc_host_attrs/fc_vport.
> 
> my initial thought was the same
> 
>>> 
>>>      In theory, given that the host stats could be updated both via signals and FPIN, one could argue that it would make sense
>>> to maintain it with the current host statistics, but keeping it confined to transport will ensure we have a uniform way of handling
>>> congestion and peer congestion events.
> 
> but then I have the same thoughts as well. And the commonization of the parsing and incrementing makes a lot more sense.
> 
>>> 
>>>     Would appreciate your thoughts there.
>>> 
>>> Thanks
>>> Shyam
>>>    
> 
> 
> I would put them under the Dynamic attributes area in fc_host_attrs and fc_rport.
> fc_host_attrs:
> fpin_cn              incremented for each Congestion Notify FPIN
> cn_sig_warn     incremented for each congestion warning signal
> cn_sig_alarm    incremented for each congestion alarm signal
> fpin_dn             incremented for each Delivery Notification FPIN where attached wwpn is local port
> fpin_li                incremented for each Link Integrity FPIN where attached wwpn is local port
> 
> fc_rport:
> fpin_dn             incremented for each Delivery Notification FPIN where attached wwpn is the rport
> fpin_li               incremented for each Link Integrity FPIN where attached wwpn is the rport
> fpin_pcn           incremented for each Peer Congestion Notify FPIN where attached wwpn is the rport
> 
> For the cn_sig_xxx values, the driver would just increment them.
> For the fpin_xxx values - we'll augment the fc_host_fpin_rcv routine to parse the fpin and increment the fc_host or fc_rport counter.

Sounds good. Will do.

Thanks
Shyam
 
> 
> -- james
Shyam Sundar July 30, 2020, 4:10 p.m. UTC | #7
James,                                                                          
                                                                                
Please review the updated patchset to see if the changes make sense to you.                
There are a few deviations from what we discussed here.           
                                                                                
I have used a single structure definition to maintain all fpin statistics       
and we are maintain them at the granularity provided by the fabric instead of
Using a single counter per category. The same    
structure is used across the fc_host_attr and the fc_rport structures.          
                                                                                
Given that the congestion signals will have to be maintained by the LLDs,       
as we do not have the plumbing to push those notifications to the transport,    
I have added them to the fc_host_statistics maintained and filled in by the     
LLD. All of the stats are displayed under the "statistics" folder under the     
sysfs.                                                                          
                                                                                
Under the host stats, a subset of stats (the Link Integrity FPINs) are          
redundunt, and are already maintained, read via the traditional mechanism.      
I have rolled them in nonetheless because during testing, they did not        
update to the same values (using the FPIN error injection commands). If we      
determine that these values will be in sync consistently, we can remove one     
of the subset at a later point.                                                 
                                                                                
Regards                                                                         
Shyam                        

> On Jun 25, 2020, at 4:25 PM, James Smart <james.smart@broadcom.com> wrote:
> 
> 
> 
> On 6/11/2020 10:42 AM, Shyam Sundar wrote:
>> Seems like this (and a previous email) never made it to the reflector, resending.
>> 
>> The suggestions make sense to me, and I have made most of the recommended changes.
>> I was looking for some guidance on placements of the sim stats structures.
>> 
>>> On May 29, 2020, at 11:53 AM, Shyam Sundar <ssundar@marvell.com> wrote:
>>> 
>>> James,
>>>      I was thinking of adding a structures for tracking the target FPIN stats.
>>> 
>>> struct fc_rport_statistics {
>>> uint32_t link_failure_count;
>>> uint32_t loss_of_sync_count;
>>> ....
>>> }
>>> 
>>> under fc_rport:
>>> 
>>> struct fc_rport {
>>> 
>>> /* Private (Transport-managed) Attributes */
>>> struct fc_rport_statistics;
>>> 
>>>      For host FPIN stats (essentially the alarm & warning), was not sure if I should add them to the fc_host_statistics or
>>> define a new structure under the Private Attributes section within the fc_host_attrs/fc_vport.
> 
> my initial thought was the same
> 
>>> 
>>>      In theory, given that the host stats could be updated both via signals and FPIN, one could argue that it would make sense
>>> to maintain it with the current host statistics, but keeping it confined to transport will ensure we have a uniform way of handling
>>> congestion and peer congestion events.
> 
> but then I have the same thoughts as well. And the commonization of the parsing and incrementing makes a lot more sense.
> 
>>> 
>>>     Would appreciate your thoughts there.
>>> 
>>> Thanks
>>> Shyam
>>>    
> 
> 
> I would put them under the Dynamic attributes area in fc_host_attrs and fc_rport.
> fc_host_attrs:
> fpin_cn              incremented for each Congestion Notify FPIN
> cn_sig_warn     incremented for each congestion warning signal
> cn_sig_alarm    incremented for each congestion alarm signal
> fpin_dn             incremented for each Delivery Notification FPIN where attached wwpn is local port
> fpin_li                incremented for each Link Integrity FPIN where attached wwpn is local port
> 
> fc_rport:
> fpin_dn             incremented for each Delivery Notification FPIN where attached wwpn is the rport
> fpin_li               incremented for each Link Integrity FPIN where attached wwpn is the rport
> fpin_pcn           incremented for each Peer Congestion Notify FPIN where attached wwpn is the rport
> 
> For the cn_sig_xxx values, the driver would just increment them.
> For the fpin_xxx values - we'll augment the fc_host_fpin_rcv routine to parse the fpin and increment the fc_host or fc_rport counter.
> 
> -- james
James Smart Sept. 21, 2020, 5:48 p.m. UTC | #8
Shyam,

comments will be posted shortly

-- james


On 7/30/2020 9:10 AM, Shyam Sundar wrote:
> James,
>                                                                                  
> Please review the updated patchset to see if the changes make sense to you.
> There are a few deviations from what we discussed here.
>                                                                                  
> I have used a single structure definition to maintain all fpin statistics
> and we are maintain them at the granularity provided by the fabric instead of
> Using a single counter per category. The same
> structure is used across the fc_host_attr and the fc_rport structures.
>                                                                                  
> Given that the congestion signals will have to be maintained by the LLDs,
> as we do not have the plumbing to push those notifications to the transport,
> I have added them to the fc_host_statistics maintained and filled in by the
> LLD. All of the stats are displayed under the "statistics" folder under the
> sysfs.
>                                                                                  
> Under the host stats, a subset of stats (the Link Integrity FPINs) are
> redundunt, and are already maintained, read via the traditional mechanism.
> I have rolled them in nonetheless because during testing, they did not
> update to the same values (using the FPIN error injection commands). If we
> determine that these values will be in sync consistently, we can remove one
> of the subset at a later point.
>                                                                                  
> Regards
> Shyam
>
>> On Jun 25, 2020, at 4:25 PM, James Smart <james.smart@broadcom.com> wrote:
>>
>>
>>
>> On 6/11/2020 10:42 AM, Shyam Sundar wrote:
>>> Seems like this (and a previous email) never made it to the reflector, resending.
>>>
>>> The suggestions make sense to me, and I have made most of the recommended changes.
>>> I was looking for some guidance on placements of the sim stats structures.
>>>
>>>> On May 29, 2020, at 11:53 AM, Shyam Sundar <ssundar@marvell.com> wrote:
>>>>
>>>> James,
>>>>       I was thinking of adding a structures for tracking the target FPIN stats.
>>>>
>>>> struct fc_rport_statistics {
>>>> uint32_t link_failure_count;
>>>> uint32_t loss_of_sync_count;
>>>> ....
>>>> }
>>>>
>>>> under fc_rport:
>>>>
>>>> struct fc_rport {
>>>>
>>>> /* Private (Transport-managed) Attributes */
>>>> struct fc_rport_statistics;
>>>>
>>>>       For host FPIN stats (essentially the alarm & warning), was not sure if I should add them to the fc_host_statistics or
>>>> define a new structure under the Private Attributes section within the fc_host_attrs/fc_vport.
>> my initial thought was the same
>>
>>>>       In theory, given that the host stats could be updated both via signals and FPIN, one could argue that it would make sense
>>>> to maintain it with the current host statistics, but keeping it confined to transport will ensure we have a uniform way of handling
>>>> congestion and peer congestion events.
>> but then I have the same thoughts as well. And the commonization of the parsing and incrementing makes a lot more sense.
>>
>>>>      Would appreciate your thoughts there.
>>>>
>>>> Thanks
>>>> Shyam
>>>>     
>>
>> I would put them under the Dynamic attributes area in fc_host_attrs and fc_rport.
>> fc_host_attrs:
>> fpin_cn              incremented for each Congestion Notify FPIN
>> cn_sig_warn     incremented for each congestion warning signal
>> cn_sig_alarm    incremented for each congestion alarm signal
>> fpin_dn             incremented for each Delivery Notification FPIN where attached wwpn is local port
>> fpin_li                incremented for each Link Integrity FPIN where attached wwpn is local port
>>
>> fc_rport:
>> fpin_dn             incremented for each Delivery Notification FPIN where attached wwpn is the rport
>> fpin_li               incremented for each Link Integrity FPIN where attached wwpn is the rport
>> fpin_pcn           incremented for each Peer Congestion Notify FPIN where attached wwpn is the rport
>>
>> For the cn_sig_xxx values, the driver would just increment them.
>> For the fpin_xxx values - we'll augment the fc_host_fpin_rcv routine to parse the fpin and increment the fc_host or fc_rport counter.
>>
>> -- james
diff mbox series

Patch

diff --git a/drivers/scsi/qla2xxx/qla_bsg.h b/drivers/scsi/qla2xxx/qla_bsg.h
index 7594fad7b5b5..0b308859047c 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.h
+++ b/drivers/scsi/qla2xxx/qla_bsg.h
@@ -290,4 +290,119 @@  struct qla_active_regions {
 	uint8_t reserved[32];
 } __packed;
 
+#define SCM_LINK_EVENT_UNKNOWN			0x0
+#define SCM_LINK_EVENT_LINK_FAILURE			0x1
+#define SCM_LINK_EVENT_LOSS_OF_SYNC			0x2
+#define SCM_LINK_EVENT_LOSS_OF_SIGNAL		0x3
+#define SCM_LINK_EVENT_PRIMITIVE_SEQ_PROTOCOL_ERROR	0x4
+#define SCM_LINK_EVENT_INVALID_TX_WORD		0x5
+#define SCM_LINK_EVENT_INVALID_CRC			0x6
+#define SCM_LINK_EVENT_DEVICE_SPECIFIC		0xF
+#define SCM_LINK_EVENT_V1_SIZE			20
+struct qla_scm_link_event {
+	uint64_t	timestamp;
+	uint16_t	event_type;
+	uint16_t	event_modifier;
+	uint32_t	event_threshold;
+	uint32_t	event_count;
+	uint8_t		reserved[12];
+} __packed;
+
+#define SCM_DELIVERY_REASON_UNKNOWN		0x0
+#define SCM_DELIVERY_REASON_TIMEOUT		0x1
+#define SCM_DELIVERY_REASON_UNABLE_TO_ROUTE	0x2
+#define SCM_DELIVERY_REASON_DEVICE_SPECIFIC	0xF
+struct qla_scm_delivery_event {
+	uint64_t	timestamp;
+	uint32_t	delivery_reason;
+	uint8_t		deliver_frame_hdr[24];
+	uint8_t		reserved[28];
+
+} __packed;
+
+#define SCM_CONGESTION_EVENT_CLEAR		0x0
+#define SCM_CONGESTION_EVENT_LOST_CREDIT	0x1
+#define SCM_CONGESTION_EVENT_CREDIT_STALL	0x2
+#define SCM_CONGESTION_EVENT_OVERSUBSCRIPTION	0x3
+#define SCM_CONGESTION_EVENT_DEVICE_SPECIFIC	0xF
+struct qla_scm_peer_congestion_event {
+	uint64_t	timestamp;
+	uint16_t	event_type;
+	uint16_t	event_modifier;
+	uint32_t	event_period;
+	uint8_t		reserved[16];
+} __packed;
+
+#define SCM_CONGESTION_SEVERITY_WARNING	0xF1
+#define SCM_CONGESTION_SEVERITY_ERROR	0xF7
+struct qla_scm_congestion_event {
+	uint64_t	timestamp;
+	uint16_t	event_type;
+	uint16_t	event_modifier;
+	uint32_t	event_period;
+	uint8_t		severity;
+	uint8_t		reserved[15];
+} __packed;
+
+#define SCM_FLAG_RDF_REJECT		0x00
+#define SCM_FLAG_RDF_COMPLETED		0x01
+#define SCM_FLAG_BROCADE_CONNECTED	0x02
+#define SCM_FLAG_CISCO_CONNECTED	0x04
+
+#define SCM_STATE_CONGESTION	0x1
+#define SCM_STATE_DELIVERY		0x2
+#define SCM_STATE_LINK_INTEGRITY	0x4
+#define SCM_STATE_PEER_CONGESTION	0x8
+
+#define QLA_CON_PRIMITIVE_RECEIVED	0x1
+#define QLA_CONGESTION_ARB_WARNING	0x1
+#define QLA_CONGESTION_ARB_ALARM	0X2
+struct qla_scm_port {
+	uint32_t			current_events;
+
+	struct qla_scm_link_event	link_integrity;
+	struct qla_scm_delivery_event	delivery;
+	struct qla_scm_congestion_event	congestion;
+	uint64_t			scm_congestion_alarm;
+	uint64_t			scm_congestion_warning;
+	uint8_t				scm_fabric_connection_flags;
+	uint8_t				reserved[43];
+} __packed;
+
+struct qla_scm_target {
+	uint8_t		wwpn[8];
+	uint32_t	current_events;
+
+	struct qla_scm_link_event		link_integrity;
+	struct qla_scm_delivery_event		delivery;
+	struct qla_scm_peer_congestion_event	peer_congestion;
+
+	uint32_t	link_failure_count;
+	uint32_t	loss_of_sync_count;
+	uint32_t        loss_of_signals_count;
+	uint32_t        primitive_seq_protocol_err_count;
+	uint32_t        invalid_transmission_word_count;
+	uint32_t        invalid_crc_count;
+
+	uint32_t        delivery_failure_unknown;
+	uint32_t        delivery_timeout;
+	uint32_t        delivery_unable_to_route;
+	uint32_t        delivery_failure_device_specific;
+
+	uint32_t        peer_congestion_clear;
+	uint32_t        peer_congestion_lost_credit;
+	uint32_t        peer_congestion_credit_stall;
+	uint32_t        peer_congestion_oversubscription;
+	uint32_t        peer_congestion_device_specific;
+	uint32_t	link_unknown_event;
+	uint32_t	link_device_specific_event;
+	uint8_t		reserved[48];
+} __packed;
+
+#define QLA_DRV_ATTR_SCM_SUPPORTED	0x00800000
+struct qla_drv_attr {
+	uint32_t	attributes;
+	uint8_t		reserved[28];
+} __packed;
+
 #endif
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index 1206f7c1ce6a..8af26be684d4 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -11,10 +11,8 @@ 
  * ----------------------------------------------------------------------
  * |             Level            |   Last Value Used  |     Holes	|
  * ----------------------------------------------------------------------
- * | Module Init and Probe        |       0x0193       | 0x0146         |
- * |                              |                    | 0x015b-0x0160	|
- * |                              |                    | 0x016e		|
- * | Mailbox commands             |       0x1206       | 0x11a2-0x11ff	|
+ * | Module Init and Probe        |       0x0199       |                |
+ * | Mailbox commands             |       0x1206       | 0x11a5-0x11ff	|
  * | Device Discovery             |       0x2134       | 0x210e-0x2116  |
  * |				  | 		       | 0x211a         |
  * |                              |                    | 0x211c-0x2128  |
@@ -26,11 +24,7 @@ 
  * |                              |                    | 0x3036,0x3038  |
  * |                              |                    | 0x303a		|
  * | DPC Thread                   |       0x4023       | 0x4002,0x4013  |
- * | Async Events                 |       0x5090       | 0x502b-0x502f  |
- * |				  | 		       | 0x5047         |
- * |                              |                    | 0x5084,0x5075	|
- * |                              |                    | 0x503d,0x5044  |
- * |                              |                    | 0x505f		|
+ * | Async Events                 |       0x509c       |                |
  * | Timer Routines               |       0x6012       |                |
  * | User Space Interactions      |       0x70e3       | 0x7018,0x702e  |
  * |				  |		       | 0x7020,0x7024  |
@@ -2752,7 +2746,6 @@  ql_dump_regs(uint level, scsi_qla_host_t *vha, uint id)
 		    "mbox[%d] %#04x\n", i, RD_REG_WORD(mbx_reg));
 }
 
-
 void
 ql_dump_buffer(uint level, scsi_qla_host_t *vha, uint id, const void *buf,
 	       uint size)
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 954d1a230b8a..882961d7246b 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -1018,6 +1018,7 @@  static inline bool qla2xxx_is_valid_mbs(unsigned int mbs)
 #define MBA_LIP_F8		0x8016	/* Received a LIP F8. */
 #define MBA_LOOP_INIT_ERR	0x8017	/* Loop Initialization Error. */
 #define MBA_FABRIC_AUTH_REQ	0x801b	/* Fabric Authentication Required. */
+#define MBA_CONGN_NOTI_RECV	0x801e	/* Congestion Notification Received */
 #define MBA_SCSI_COMPLETION	0x8020	/* SCSI Command Complete. */
 #define MBA_CTIO_COMPLETION	0x8021	/* CTIO Complete. */
 #define MBA_IP_COMPLETION	0x8022	/* IP Transmit Command Complete. */
@@ -1479,6 +1480,25 @@  typedef struct {
 	uint8_t  reserved_3[26];
 } init_cb_t;
 
+/* Special Features Control Block */
+struct init_sf_cb {
+	uint8_t	format;
+	uint8_t	reserved0;
+	/*
+	 * BIT 15-14 = Reserved
+	 * BIT_13 = SAN Congestion Management (1 - Enabled, 0 - Disabled)
+	 * BIT_12 = Remote Write Optimization (1 - Enabled, 0 - Disabled)
+	 * BIT 11-0 = Reserved
+	 */
+	uint16_t flags;
+	uint8_t	reserved1[32];
+	uint16_t discard_OHRB_timeout_value;
+	uint16_t remote_write_opt_queue_num;
+	uint8_t	reserved2[40];
+	uint8_t scm_related_parameter[16];
+	uint8_t reserved3[32];
+};
+
 /*
  * Get Link Status mailbox command return buffer.
  */
@@ -2152,6 +2172,52 @@  typedef struct {
 	struct dsd64 rsp_dsd;
 } ms_iocb_entry_t;
 
+#define SCM_EDC_ACC_RECEIVED		BIT_6
+#define SCM_RDF_ACC_RECEIVED		BIT_7
+#define SCM_NOTIFICATION_TYPE_LINK_INTEGRITY	0x00020001
+#define SCM_NOTIFICATION_TYPE_DELIVERY		0x00020002
+#define SCM_NOTIFICATION_TYPE_PEER_CONGESTION	0x00020003
+#define SCM_NOTIFICATION_TYPE_CONGESTION	0x00020004
+#define FPIN_DESCRIPTOR_HEADER_SIZE	4
+#define FPIN_ELS_DESCRIPTOR_LIST_OFFSET	8
+struct fpin_descriptor {
+	__be32 descriptor_tag;
+	__be32 descriptor_length;
+	union {
+		uint8_t common_detecting_port_name[WWN_SIZE];
+		struct {
+			uint8_t detecting_port_name[WWN_SIZE];
+			uint8_t attached_port_name[WWN_SIZE];
+			__be16 event_type;
+			__be16 event_modifier;
+			__be32 event_threshold;
+			__be32 event_count;
+			__be32 port_name_count;
+			uint8_t port_name_list[1][WWN_SIZE];
+		} link_integrity;
+		struct {
+			uint8_t detecting_port_name[WWN_SIZE];
+			uint8_t attached_port_name[WWN_SIZE];
+			__be32 delivery_reason_code;
+		} delivery;
+		struct {
+			uint8_t detecting_port_name[WWN_SIZE];
+			uint8_t attached_port_name[WWN_SIZE];
+			__be16 event_type;
+			__be16 event_modifier;
+			__be32 event_period;
+			__be32 port_name_count;
+			uint8_t port_name_list[1][WWN_SIZE];
+		} peer_congestion;
+		struct {
+			__be16 event_type;
+			__be16 event_modifier;
+			__be32 event_period;
+			uint8_t severity;
+			uint8_t reserved[3];
+		} congestion;
+	};
+};
 
 /*
  * ISP queue - Mailbox Command entry structure definition.
@@ -2412,6 +2478,7 @@  typedef struct fc_port {
 	unsigned int n2n_flag:1;
 	unsigned int explicit_logout:1;
 	unsigned int prli_pend_timer:1;
+	unsigned int slow_device:1;
 
 	struct completion nvme_del_done;
 	uint32_t nvme_prli_service_param;
@@ -2491,6 +2558,8 @@  typedef struct fc_port {
 	u8 last_login_state;
 	u16 n2n_link_reset_cnt;
 	u16 n2n_chip_reset;
+
+	struct qla_scm_target scm_stats;
 } fc_port_t;
 
 enum {
@@ -3821,6 +3890,12 @@  struct qla_hw_data {
 		uint32_t        n2n_bigger:1;
 		uint32_t	secure_adapter:1;
 		uint32_t	secure_fw:1;
+				/* Supported by Adapter */
+		uint32_t	scm_supported_a:1;
+				/* Supported by Firmware */
+		uint32_t	scm_supported_f:1;
+				/* Enabled in Driver */
+		uint32_t	scm_enabled:1;
 	} flags;
 
 	uint16_t max_exchg;
@@ -4138,6 +4213,13 @@  struct qla_hw_data {
 	int		init_cb_size;
 	dma_addr_t	ex_init_cb_dma;
 	struct ex_init_cb_81xx *ex_init_cb;
+	dma_addr_t	sf_init_cb_dma;
+	struct init_sf_cb *sf_init_cb;
+
+	void		*scm_fpin_els_buff;
+	uint64_t	scm_fpin_els_buff_size;
+	bool		scm_fpin_valid;
+	bool		scm_fpin_payload_size;
 
 	void		*async_pd;
 	dma_addr_t	async_pd_dma;
@@ -4200,6 +4282,12 @@  struct qla_hw_data {
 #define FW_ATTR_H_NVME		BIT_10
 #define FW_ATTR_H_NVME_UPDATED  BIT_14
 
+	/* About firmware SCM support */
+#define FW_ATTR_EXT0_SCM_SUPPORTED	BIT_12
+	/* Brocade fabric attached */
+#define FW_ATTR_EXT0_SCM_BROCADE	0x00001000
+	/* Cisco fabric attached */
+#define FW_ATTR_EXT0_SCM_CISCO		0x00002000
 	uint16_t	fw_attributes_ext[2];
 	uint32_t	fw_memory_size;
 	uint32_t	fw_transfer_size;
@@ -4718,6 +4806,7 @@  typedef struct scsi_qla_host {
 	__le16 dport_data[4];
 	struct list_head gpnid_list;
 	struct fab_scan scan;
+	struct qla_scm_port scm_stats;
 
 	unsigned int irq_offset;
 } scsi_qla_host_t;
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index f9bad5bd7198..fa45fc7e6e2b 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -723,6 +723,9 @@  struct ct_entry_24xx {
 	struct dsd64 dsd[2];
 };
 
+#define PURX_ELS_HEADER_SIZE	0x18
+#define FPIN_ELS_DESCRIPTOR_LIST_OFFSET	8
+
 /*
  * ISP queue - PUREX IOCB entry structure definition
  */
@@ -2020,7 +2023,9 @@  struct nvram_81xx {
 	 * BIT 0    = Extended BB credits for LR
 	 * BIT 1    = Virtual Fabric Enable
 	 * BIT 2-5  = Distance Support if BIT 0 is on
-	 * BIT 6-15 = Unused
+	 * BIT 6    = Prefer FCP
+	 * BIT 7    = SCM Disabled if BIT is set (1)
+	 * BIT 8-15 = Unused
 	 */
 	uint16_t enhanced_features;
 
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 54d82f7d478f..3ba21368d5b6 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -127,6 +127,7 @@  int qla_post_iidma_work(struct scsi_qla_host *vha, fc_port_t *fcport);
 void qla_do_iidma_work(struct scsi_qla_host *vha, fc_port_t *fcport);
 int qla2x00_reserve_mgmt_server_loop_id(scsi_qla_host_t *);
 void qla_rscn_replay(fc_port_t *fcport);
+void qla24xx_free_purex_item(struct purex_item *item);
 extern bool qla24xx_risc_firmware_invalid(uint32_t *);
 
 /*
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 95b6166ae0cc..dcadf1719711 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -3752,7 +3752,7 @@  qla2x00_setup_chip(scsi_qla_host_t *vha)
 		}
 
 		/* Enable PUREX PASSTHRU */
-		if (ql2xrdpenable)
+		if (ql2xrdpenable || ha->flags.scm_supported_f)
 			qla25xx_set_els_cmds_supported(vha);
 	} else
 		goto failed;
@@ -3965,7 +3965,7 @@  qla24xx_update_fw_options(scsi_qla_host_t *vha)
 			ha->fw_options[2] &= ~BIT_8;
 	}
 
-	if (ql2xrdpenable)
+	if (ql2xrdpenable || ha->flags.scm_supported_f)
 		ha->fw_options[1] |= ADD_FO1_ENABLE_PUREX_IOCB;
 
 	/* Enable Async 8130/8131 events -- transceiver insertion/removal */
@@ -8514,6 +8514,11 @@  qla81xx_nvram_config(scsi_qla_host_t *vha)
 		icb->node_name[0] &= 0xF0;
 	}
 
+	if (IS_QLA28XX(ha) || IS_QLA27XX(ha)) {
+		if ((le16_to_cpu(nv->enhanced_features) & BIT_7) == 0)
+			ha->flags.scm_supported_a = 1;
+	}
+
 	/* Set host adapter parameters. */
 	ha->flags.disable_risc_code_load = 0;
 	ha->flags.enable_lip_reset = 0;
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 401ce0023cd5..7ba8a881fd4b 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -23,6 +23,372 @@  static void qla2x00_status_cont_entry(struct rsp_que *, sts_cont_entry_t *);
 static int qla2x00_error_entry(scsi_qla_host_t *, struct rsp_que *,
 	sts_entry_t *);
 
+void
+qla_link_integrity_tgt_stats_update(struct fpin_descriptor *fpin_desc,
+				    fc_port_t *fcport)
+{
+	ql_log(ql_log_info, fcport->vha, 0x502d,
+	       "Link Integrity Event Type %d for Port %8phN\n",
+	       be16_to_cpu(fpin_desc->link_integrity.event_type),
+	       fcport->port_name);
+
+	fcport->scm_stats.link_integrity.event_type =
+	    be16_to_cpu(fpin_desc->link_integrity.event_type);
+	fcport->scm_stats.link_integrity.event_modifier =
+	    be16_to_cpu(fpin_desc->link_integrity.event_modifier);
+	fcport->scm_stats.link_integrity.event_threshold =
+	    be32_to_cpu(fpin_desc->link_integrity.event_threshold);
+	fcport->scm_stats.link_integrity.event_count =
+	    be32_to_cpu(fpin_desc->link_integrity.event_count);
+	fcport->scm_stats.link_integrity.timestamp = ktime_get_real_seconds();
+
+	fcport->scm_stats.current_events |= SCM_STATE_LINK_INTEGRITY;
+	switch (be16_to_cpu(fpin_desc->link_integrity.event_type)) {
+	case SCM_LINK_EVENT_UNKNOWN:
+		fcport->scm_stats.link_unknown_event +=
+		    be32_to_cpu(fpin_desc->link_integrity.event_count);
+		break;
+	case SCM_LINK_EVENT_LINK_FAILURE:
+		fcport->scm_stats.link_failure_count +=
+		    be32_to_cpu(fpin_desc->link_integrity.event_count);
+		break;
+	case SCM_LINK_EVENT_LOSS_OF_SYNC:
+		fcport->scm_stats.loss_of_sync_count +=
+		    be32_to_cpu(fpin_desc->link_integrity.event_count);
+		break;
+	case SCM_LINK_EVENT_LOSS_OF_SIGNAL:
+		fcport->scm_stats.loss_of_signals_count +=
+		    be32_to_cpu(fpin_desc->link_integrity.event_count);
+		break;
+	case SCM_LINK_EVENT_PRIMITIVE_SEQ_PROTOCOL_ERROR:
+		fcport->scm_stats.primitive_seq_protocol_err_count +=
+		    be32_to_cpu(fpin_desc->link_integrity.event_count);
+		break;
+	case SCM_LINK_EVENT_INVALID_TX_WORD:
+		fcport->scm_stats.invalid_transmission_word_count +=
+		    be32_to_cpu(fpin_desc->link_integrity.event_count);
+		break;
+	case SCM_LINK_EVENT_INVALID_CRC:
+		fcport->scm_stats.invalid_crc_count +=
+		    be32_to_cpu(fpin_desc->link_integrity.event_count);
+		break;
+	case SCM_LINK_EVENT_DEVICE_SPECIFIC:
+		fcport->scm_stats.link_device_specific_event +=
+		    be32_to_cpu(fpin_desc->link_integrity.event_count);
+		break;
+	}
+}
+
+void
+qla_scm_process_link_integrity_d(struct scsi_qla_host *vha,
+				 struct fpin_descriptor *fpin_desc)
+{
+	int i;
+	fc_port_t *fcport =  NULL;
+	fc_port_t *d_fcport = NULL, *a_fcport = NULL;
+
+	fcport = qla2x00_find_fcport_by_wwpn(vha,
+				fpin_desc->link_integrity.detecting_port_name,
+				0);
+	if (fcport) {
+		d_fcport = fcport;
+		qla_link_integrity_tgt_stats_update(fpin_desc, fcport);
+	}
+
+	fcport = qla2x00_find_fcport_by_wwpn(vha,
+				fpin_desc->link_integrity.attached_port_name,
+				0);
+	if (fcport) {
+		a_fcport = fcport;
+		qla_link_integrity_tgt_stats_update(fpin_desc, fcport);
+	}
+
+	if (fpin_desc->link_integrity.port_name_count > 0) {
+		for (i = 0;
+		    i < be32_to_cpu(fpin_desc->link_integrity.port_name_count);
+		    i++) {
+			fcport = qla2x00_find_fcport_by_wwpn(vha,
+				fpin_desc->link_integrity.port_name_list[i],
+				0);
+			if (fcport && (fcport != d_fcport) &&
+			    (fcport != a_fcport)) {
+				qla_link_integrity_tgt_stats_update(fpin_desc,
+								    fcport);
+			}
+		}
+	}
+
+	if (memcmp(vha->port_name, fpin_desc->link_integrity.attached_port_name,
+		   WWN_SIZE) == 0) {
+		ql_log(ql_log_info, vha, 0x5093,
+		       "Link Integrity Event Type %d for HBA WWN %8phN\n",
+		       be16_to_cpu(fpin_desc->link_integrity.event_type),
+		       vha->port_name);
+
+		vha->scm_stats.current_events |= SCM_STATE_LINK_INTEGRITY;
+		vha->scm_stats.link_integrity.event_type =
+		    be16_to_cpu(fpin_desc->link_integrity.event_type);
+		vha->scm_stats.link_integrity.event_modifier =
+		    be16_to_cpu(fpin_desc->link_integrity.event_modifier);
+		vha->scm_stats.link_integrity.event_threshold =
+		    be32_to_cpu(fpin_desc->link_integrity.event_threshold);
+		vha->scm_stats.link_integrity.event_count =
+		    be32_to_cpu(fpin_desc->link_integrity.event_count);
+		vha->scm_stats.link_integrity.timestamp =
+		    ktime_get_real_seconds();
+	}
+}
+
+void
+qla_delivery_tgt_stats_update(struct fpin_descriptor *fpin_desc,
+			      fc_port_t *fcport)
+{
+	ql_log(ql_log_info, fcport->vha, 0x5095,
+	       "Delivery Notification Reason Code %d for Port %8phN\n",
+	       be32_to_cpu(fpin_desc->delivery.delivery_reason_code),
+	       fcport->port_name);
+
+	switch (be32_to_cpu(fpin_desc->delivery.delivery_reason_code)) {
+	case SCM_DELIVERY_REASON_UNKNOWN:
+		fcport->scm_stats.delivery_failure_unknown++;
+		break;
+	case SCM_DELIVERY_REASON_TIMEOUT:
+		fcport->scm_stats.delivery_timeout++;
+		break;
+	case SCM_DELIVERY_REASON_UNABLE_TO_ROUTE:
+		fcport->scm_stats.delivery_unable_to_route++;
+		break;
+	case SCM_DELIVERY_REASON_DEVICE_SPECIFIC:
+		fcport->scm_stats.delivery_failure_device_specific++;
+		break;
+	}
+	fcport->scm_stats.delivery.timestamp =
+	    ktime_get_real_seconds();
+}
+
+/*
+ * Process Delivery Notification Descriptor
+ */
+void
+qla_scm_process_delivery_notification_d(struct scsi_qla_host *vha,
+					struct fpin_descriptor *fpin_desc)
+{
+	fc_port_t *fcport =  NULL;
+
+	fcport = qla2x00_find_fcport_by_wwpn(vha,
+				fpin_desc->delivery.detecting_port_name, 0);
+	if (fcport)
+		qla_delivery_tgt_stats_update(fpin_desc, fcport);
+
+	fcport = qla2x00_find_fcport_by_wwpn(vha,
+				fpin_desc->delivery.attached_port_name, 0);
+	if (fcport)
+		qla_delivery_tgt_stats_update(fpin_desc, fcport);
+
+	if (memcmp(vha->port_name, fpin_desc->delivery.attached_port_name,
+		   WWN_SIZE) == 0) {
+		ql_log(ql_log_info, vha, 0x5096,
+		       "Delivery Notification Reason Code %d for HBA WWN %8phN\n",
+		       be32_to_cpu(fpin_desc->delivery.delivery_reason_code),
+		       vha->port_name);
+
+		vha->scm_stats.delivery.delivery_reason =
+		    be32_to_cpu(fpin_desc->delivery.delivery_reason_code);
+		vha->scm_stats.current_events |= SCM_STATE_DELIVERY;
+		vha->scm_stats.delivery.timestamp =
+		    ktime_get_real_seconds();
+	}
+}
+
+void
+qla_scm_set_target_device_state(fc_port_t *fcport,
+				struct fpin_descriptor *fpin_desc)
+{
+	switch (be16_to_cpu(fpin_desc->peer_congestion.event_type)) {
+	case SCM_CONGESTION_EVENT_CLEAR:
+		ql_log(ql_log_warn, fcport->vha, 0x5097,
+		       "Port %8phN Slow Device: Cleared\n", fcport->port_name);
+		fcport->slow_device = 0;
+		break;
+	case SCM_CONGESTION_EVENT_LOST_CREDIT:
+		break;
+	case SCM_CONGESTION_EVENT_CREDIT_STALL:
+	case SCM_CONGESTION_EVENT_OVERSUBSCRIPTION:
+		ql_log(ql_log_warn, fcport->vha, 0x508c,
+		       "Port %8phN Slow Device: Set\n", fcport->port_name);
+		fcport->slow_device = 1;
+		break;
+	case SCM_CONGESTION_EVENT_DEVICE_SPECIFIC:
+		break;
+	}
+}
+
+void
+qla_peer_congestion_tgt_stats_update(struct fpin_descriptor *fpin_desc,
+				     fc_port_t *fcport)
+{
+	ql_log(ql_log_info, fcport->vha, 0x5098,
+	       "Peer Congestion Event Type %d for Port %8phN\n",
+	       be16_to_cpu(fpin_desc->peer_congestion.event_type),
+	       fcport->port_name);
+
+	fcport->scm_stats.peer_congestion.timestamp = ktime_get_real_seconds();
+	fcport->scm_stats.peer_congestion.event_type =
+	    be16_to_cpu(fpin_desc->peer_congestion.event_type);
+	fcport->scm_stats.peer_congestion.event_modifier =
+	    be16_to_cpu(fpin_desc->peer_congestion.event_modifier);
+	fcport->scm_stats.peer_congestion.event_period =
+	    fpin_desc->peer_congestion.event_period;
+
+	// What is the API to get system time ?
+	fcport->scm_stats.current_events |= SCM_STATE_PEER_CONGESTION;
+	switch (be16_to_cpu(fpin_desc->peer_congestion.event_type)) {
+	case SCM_CONGESTION_EVENT_CLEAR:
+		fcport->scm_stats.peer_congestion_clear++;
+		fcport->scm_stats.current_events &= ~SCM_STATE_PEER_CONGESTION;
+		break;
+	case SCM_CONGESTION_EVENT_LOST_CREDIT:
+		fcport->scm_stats.peer_congestion_lost_credit++;
+		break;
+	case SCM_CONGESTION_EVENT_CREDIT_STALL:
+		fcport->scm_stats.peer_congestion_credit_stall++;
+		break;
+	case SCM_CONGESTION_EVENT_OVERSUBSCRIPTION:
+		fcport->scm_stats.peer_congestion_oversubscription++;
+		break;
+	case SCM_CONGESTION_EVENT_DEVICE_SPECIFIC:
+		fcport->scm_stats.peer_congestion_device_specific++;
+		break;
+	}
+	qla_scm_set_target_device_state(fcport, fpin_desc);
+}
+
+/*
+ * Process Peer-Congestion Notification Descriptor
+ */
+void
+qla_scm_process_peer_congestion_notification_d(struct scsi_qla_host *vha,
+					struct fpin_descriptor *fpin_desc)
+{
+	int i;
+	fc_port_t *fcport =  NULL;
+	fc_port_t *d_fcport = NULL, *a_fcport = NULL;
+
+	fcport = qla2x00_find_fcport_by_wwpn(vha,
+			fpin_desc->peer_congestion.detecting_port_name, 0);
+	if (fcport) {
+		d_fcport = fcport;
+		qla_peer_congestion_tgt_stats_update(fpin_desc, fcport);
+	}
+
+	fcport = qla2x00_find_fcport_by_wwpn(vha,
+			fpin_desc->peer_congestion.attached_port_name, 0);
+	if (fcport) {
+		a_fcport = fcport;
+		qla_peer_congestion_tgt_stats_update(fpin_desc, fcport);
+	}
+
+	if (be32_to_cpu(fpin_desc->peer_congestion.port_name_count) > 0) {
+		for (i = 0;
+		    i < be32_to_cpu(fpin_desc->peer_congestion.port_name_count);
+		    i++) {
+			fcport = qla2x00_find_fcport_by_wwpn(vha,
+			fpin_desc->peer_congestion.port_name_list[i], 0);
+			if (fcport && fcport != d_fcport &&
+			    fcport != a_fcport) {
+				qla_peer_congestion_tgt_stats_update(fpin_desc,
+								fcport);
+			}
+		}
+	}
+}
+
+/*
+ * qla_scm_process_congestion_notification_d() - Process
+ * Process Congestion Notification Descriptor
+ * @rsp: response queue
+ * @pkt: Entry pointer
+ */
+void
+qla_scm_process_congestion_notification_d(struct scsi_qla_host *vha,
+					  struct fpin_descriptor *fpin_desc)
+{
+	ql_log(ql_log_info, vha, 0x5099,
+	       "Congestion Notification Event Type %d\n",
+	       be16_to_cpu(fpin_desc->congestion.event_type));
+
+	vha->scm_stats.congestion.event_type =
+	    be16_to_cpu(fpin_desc->congestion.event_type);
+	vha->scm_stats.congestion.event_modifier =
+	    be16_to_cpu(fpin_desc->congestion.event_modifier);
+	vha->scm_stats.congestion.event_period =
+	    be32_to_cpu(fpin_desc->congestion.event_period);
+	vha->scm_stats.congestion.severity =
+	    fpin_desc->congestion.severity;
+
+	if (be16_to_cpu(fpin_desc->congestion.event_type) ==
+	    SCM_CONGESTION_EVENT_CLEAR)
+		vha->scm_stats.current_events &= ~SCM_STATE_CONGESTION;
+	else
+		vha->scm_stats.current_events |= SCM_STATE_CONGESTION;
+
+	if (fpin_desc->congestion.severity == SCM_CONGESTION_SEVERITY_WARNING)
+		vha->scm_stats.scm_congestion_warning++;
+	else if (fpin_desc->congestion.severity ==
+	     SCM_CONGESTION_SEVERITY_ERROR)
+		vha->scm_stats.scm_congestion_alarm++;
+}
+
+void
+qla27xx_process_purex_fpin(struct scsi_qla_host *vha, struct purex_item *item)
+{
+	struct fpin_descriptor *fpin_desc;
+	uint16_t fpin_desc_len;
+	uint32_t fpin_offset = 0;
+	void *pkt = &item->iocb;
+	uint16_t pkt_size = item->size;
+
+	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x508d,
+	       "%s: Enter\n", __func__);
+
+	ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x508e,
+	       "-------- ELS REQ -------\n");
+	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x508f,
+		       pkt, pkt_size);
+
+	fpin_desc = (struct fpin_descriptor *)((uint8_t *)pkt +
+	    FPIN_ELS_DESCRIPTOR_LIST_OFFSET);
+
+	fpin_desc_len = pkt_size - FPIN_ELS_DESCRIPTOR_LIST_OFFSET;
+	while (fpin_offset <= fpin_desc_len) {
+		fpin_desc = (struct fpin_descriptor *)((uint8_t *)fpin_desc +
+		    fpin_offset);
+
+		if (fpin_offset >= fpin_desc_len - 8)
+			return;
+
+		switch (be32_to_cpu(fpin_desc->descriptor_tag)) {
+		case SCM_NOTIFICATION_TYPE_LINK_INTEGRITY:
+			qla_scm_process_link_integrity_d(vha, fpin_desc);
+			break;
+		case SCM_NOTIFICATION_TYPE_DELIVERY:
+			qla_scm_process_delivery_notification_d(vha, fpin_desc);
+			break;
+		case SCM_NOTIFICATION_TYPE_PEER_CONGESTION:
+			qla_scm_process_peer_congestion_notification_d(vha,
+								fpin_desc);
+			break;
+		case SCM_NOTIFICATION_TYPE_CONGESTION:
+			qla_scm_process_congestion_notification_d(vha,
+								fpin_desc);
+			break;
+		}
+		fpin_offset += be32_to_cpu(fpin_desc->descriptor_length) +
+		    FPIN_DESCRIPTOR_HEADER_SIZE;
+	}
+	fc_host_fpin_rcv(vha->host, pkt_size, (char *)pkt);
+}
+
 const char *const port_state_str[] = {
 	"Unknown",
 	"UNCONFIGURED",
@@ -836,6 +1202,111 @@  struct purex_item
 	return item;
 }
 
+/**
+ * qla27xx_copy_fpin_pkt() - Copy over fpin packets that can
+ * span over multiple IOCBs.
+ * @vha: SCSI driver HA context
+ * @pkt: ELS packet
+ * @rsp: Response queue
+ */
+struct purex_item *
+qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt,
+		      struct rsp_que **rsp)
+{
+	struct purex_entry_24xx *purex = *pkt;
+	struct rsp_que *rsp_q = *rsp;
+	sts_cont_entry_t *new_pkt;
+	uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0;
+	uint16_t buffer_copy_offset = 0;
+	uint16_t entry_count, entry_count_remaining;
+	struct purex_item *item;
+	void *fpin_pkt = NULL;
+
+	total_bytes = le16_to_cpu(purex->frame_size & 0x0FFF)
+	    - PURX_ELS_HEADER_SIZE;
+	pending_bytes = total_bytes;
+	entry_count = entry_count_remaining = purex->entry_count;
+	no_bytes = (pending_bytes > sizeof(purex->els_frame_payload))  ?
+		   sizeof(purex->els_frame_payload) : pending_bytes;
+	ql_log(ql_log_info, vha, 0x509a,
+	       "FPIN ELS, frame_size 0x%x, entry count %d\n",
+	       total_bytes, entry_count);
+
+	item = qla24xx_alloc_purex_item(vha, total_bytes);
+	if (!item)
+		return item;
+
+	fpin_pkt = &item->iocb;
+
+	memcpy(fpin_pkt, &purex->els_frame_payload[0], no_bytes);
+	buffer_copy_offset += no_bytes;
+	pending_bytes -= no_bytes;
+	--entry_count_remaining;
+
+	((response_t *)purex)->signature = RESPONSE_PROCESSED;
+	wmb();
+
+	do {
+		while ((total_bytes > 0) && (entry_count_remaining > 0)) {
+			if (rsp_q->ring_ptr->signature == RESPONSE_PROCESSED) {
+				ql_dbg(ql_dbg_async, vha, 0x5084,
+				       "Ran out of IOCBs, partial data 0x%x\n",
+				       buffer_copy_offset);
+				cpu_relax();
+				continue;
+			}
+
+			new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
+			*pkt = new_pkt;
+
+			if (new_pkt->entry_type != STATUS_CONT_TYPE) {
+				ql_log(ql_log_warn, vha, 0x507a,
+				       "Unexpected IOCB type, partial data 0x%x\n",
+				       buffer_copy_offset);
+				break;
+			}
+
+			rsp_q->ring_index++;
+			if (rsp_q->ring_index == rsp_q->length) {
+				rsp_q->ring_index = 0;
+				rsp_q->ring_ptr = rsp_q->ring;
+			} else {
+				rsp_q->ring_ptr++;
+			}
+			no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
+			    sizeof(new_pkt->data) : pending_bytes;
+			if ((buffer_copy_offset + no_bytes) <= total_bytes) {
+				memcpy(((uint8_t *)fpin_pkt +
+				    buffer_copy_offset), new_pkt->data,
+				    no_bytes);
+				buffer_copy_offset += no_bytes;
+				pending_bytes -= no_bytes;
+				--entry_count_remaining;
+			} else {
+				ql_log(ql_log_warn, vha, 0x5044,
+				       "Attempt to copy more that we got, optimizing..%x\n",
+				       buffer_copy_offset);
+				memcpy(((uint8_t *)fpin_pkt +
+				    buffer_copy_offset), new_pkt->data,
+				    total_bytes - buffer_copy_offset);
+			}
+
+			((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
+			wmb();
+		}
+
+		if (pending_bytes != 0 || entry_count_remaining != 0) {
+			ql_log(ql_log_fatal, vha, 0x508b,
+			       "Dropping partial FPIN, underrun bytes = 0x%x, entry cnts 0x%x\n",
+			       total_bytes, entry_count_remaining);
+			qla24xx_free_purex_item(item);
+			return NULL;
+		}
+	} while (entry_count_remaining > 0);
+	host_to_fcp_swap((uint8_t *)&item->iocb, total_bytes);
+	return item;
+}
+
 /**
  * qla2x00_async_event() - Process aynchronous events.
  * @vha: SCSI driver HA context
@@ -1350,6 +1821,19 @@  qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
 			qla2x00_post_aen_work(vha, FCH_EVT_RSCN, rscn_entry);
 		}
 		break;
+	case MBA_CONGN_NOTI_RECV:
+		if (!ha->flags.scm_enabled ||
+		    mb[1] != QLA_CON_PRIMITIVE_RECEIVED)
+			break;
+
+		if (mb[2] == QLA_CONGESTION_ARB_WARNING) {
+			vha->scm_stats.scm_congestion_warning++;
+		} else if (mb[2] == QLA_CONGESTION_ARB_ALARM) {
+			ql_log(ql_log_warn, vha, 0x509b,
+			       "Congestion Alarm %04x %04x.\n", mb[1], mb[2]);
+			vha->scm_stats.scm_congestion_alarm++;
+		}
+		break;
 	/* case MBA_RIO_RESPONSE: */
 	case MBA_ZIO_RESPONSE:
 		ql_dbg(ql_dbg_async, vha, 0x5015,
@@ -3279,6 +3763,7 @@  void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 {
 	struct sts_entry_24xx *pkt;
 	struct qla_hw_data *ha = vha->hw;
+	struct purex_entry_24xx *purex_entry;
 	struct purex_item *pure_item;
 
 	if (!ha->flags.fw_started)
@@ -3334,7 +3819,6 @@  void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 				pure_item = qla24xx_copy_std_pkt(vha, pkt);
 				if (!pure_item)
 					break;
-
 				qla24xx_queue_purex_item(vha, pure_item,
 							 qla24xx_process_abts);
 				break;
@@ -3384,29 +3868,41 @@  void qla24xx_process_response_queue(struct scsi_qla_host *vha,
 			    (struct vp_ctrl_entry_24xx *)pkt);
 			break;
 		case PUREX_IOCB_TYPE:
-		{
-			struct purex_entry_24xx *purex = (void *)pkt;
-
-			if (purex->els_frame_payload[3] != ELS_COMMAND_RDP) {
-				ql_dbg(ql_dbg_init, vha, 0x5091,
-				    "Discarding ELS Request opcode %#x...\n",
-				    purex->els_frame_payload[3]);
+			purex_entry = (void *)pkt;
+			switch (purex_entry->els_frame_payload[3]) {
+			case ELS_COMMAND_RDP:
+				pure_item = qla24xx_copy_std_pkt(vha, pkt);
+				if (!pure_item)
+					break;
+				qla24xx_queue_purex_item(vha, pure_item,
+						 qla24xx_process_purex_rdp);
 				break;
-			}
-			pure_item = qla24xx_copy_std_pkt(vha, pkt);
-			if (!pure_item)
+			case ELS_COMMAND_FPIN:
+				if (!vha->hw->flags.scm_enabled) {
+					ql_log(ql_log_warn, vha, 0x5094,
+					       "SCM not active for this port\n");
+					break;
+				}
+				pure_item = qla27xx_copy_fpin_pkt(vha,
+							  (void **)&pkt, &rsp);
+				if (!pure_item)
+					break;
+				qla24xx_queue_purex_item(vha, pure_item,
+						 qla27xx_process_purex_fpin);
 				break;
-
-			qla24xx_queue_purex_item(vha, pure_item,
-						 qla24xx_process_purex_rdp);
+			case ELS_COMMAND_PUN:
+				break;
+			default:
+				ql_log(ql_log_warn, vha, 0x509c,
+				       "Discarding ELS Request opcode 0x%x\n",
+				       purex_entry->els_frame_payload[3]);
+			}
 			break;
-		}
 		default:
 			/* Type Not Supported. */
 			ql_dbg(ql_dbg_async, vha, 0x5042,
-			    "Received unknown response pkt type %x "
-			    "entry status=%x.\n",
-			    pkt->entry_type, pkt->entry_status);
+			       "Received unknown response pkt type 0x%x entry status=%x.\n",
+			       pkt->entry_type, pkt->entry_status);
 			break;
 		}
 		((response_t *)pkt)->signature = RESPONSE_PROCESSED;
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 76a4f2bfdbed..3c411b5914ce 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -1126,6 +1126,16 @@  qla2x00_get_fw_version(scsi_qla_host_t *vha)
 			    (ha->flags.secure_fw) ? "Supported" :
 			    "Not Supported");
 		}
+
+		if (ha->flags.scm_supported_a &&
+		    (ha->fw_attributes_ext[0] & FW_ATTR_EXT0_SCM_SUPPORTED)) {
+			ha->flags.scm_supported_f = 1;
+			memset(ha->sf_init_cb, 0, sizeof(struct init_sf_cb));
+			ha->sf_init_cb->flags |= BIT_13;
+		}
+		ql_log(ql_log_info, vha, 0x11a3, "SCM in FW: %s\n",
+		       (ha->flags.scm_supported_f) ? "Supported" :
+		       "Not Supported");
 	}
 
 failed:
@@ -1635,8 +1645,11 @@  qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
 		mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10;
 	if (IS_FWI2_CAPABLE(vha->hw))
 		mcp->in_mb |= MBX_19|MBX_18|MBX_17|MBX_16;
-	if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw))
+	if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw)) {
 		mcp->in_mb |= MBX_15;
+		mcp->out_mb |= MBX_7|MBX_21|MBX_22|MBX_23;
+	}
+
 	mcp->tov = MBX_TOV_SECONDS;
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
@@ -1689,8 +1702,22 @@  qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
 			}
 		}
 
-		if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw))
+		if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw)) {
 			vha->bbcr = mcp->mb[15];
+			if (mcp->mb[7] & SCM_EDC_ACC_RECEIVED) {
+				ql_log(ql_log_info, vha, 0x11a4,
+				       "SCM: EDC ELS completed, flags 0x%x\n",
+				       mcp->mb[21]);
+			}
+			if (mcp->mb[7] & SCM_RDF_ACC_RECEIVED) {
+				vha->hw->flags.scm_enabled = 1;
+				vha->scm_stats.scm_fabric_connection_flags |=
+				    SCM_FLAG_RDF_COMPLETED;
+				ql_log(ql_log_info, vha, 0x11a5,
+				       "SCM: RDF ELS completed, flags 0x%x\n",
+				       mcp->mb[23]);
+			}
+		}
 	}
 
 	return rval;
@@ -1803,6 +1830,17 @@  qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
 		mcp->mb[14] = sizeof(*ha->ex_init_cb);
 		mcp->out_mb |= MBX_14|MBX_13|MBX_12|MBX_11|MBX_10;
 	}
+
+	if (ha->flags.scm_supported_f) {
+		mcp->mb[1] |= BIT_1;
+		mcp->mb[16] = MSW(ha->sf_init_cb_dma);
+		mcp->mb[17] = LSW(ha->sf_init_cb_dma);
+		mcp->mb[18] = MSW(MSD(ha->sf_init_cb_dma));
+		mcp->mb[19] = LSW(MSD(ha->sf_init_cb_dma));
+		mcp->mb[15] = sizeof(*ha->sf_init_cb);
+		mcp->out_mb |= MBX_19|MBX_18|MBX_17|MBX_16|MBX_15;
+	}
+
 	/* 1 and 2 should normally be captured. */
 	mcp->in_mb = MBX_2|MBX_1|MBX_0;
 	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
@@ -4902,7 +4940,8 @@  qla25xx_set_els_cmds_supported(scsi_qla_host_t *vha)
 
 	/* List of Purex ELS */
 	cmd_opcode[0] = ELS_COMMAND_FPIN;
-	cmd_opcode[1] = ELS_COMMAND_PUN;
+	cmd_opcode[1] = ELS_COMMAND_RDP;
+	cmd_opcode[2] = ELS_COMMAND_PUN;
 
 	for (i = 0; i < PUREX_CMD_COUNT; i++) {
 		index = cmd_opcode[i] / 8;
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 007f39128dbf..70241506d0ca 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -4220,6 +4220,16 @@  qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 		    "ex_init_cb=%p.\n", ha->ex_init_cb);
 	}
 
+	/* Get consistent memory allocated for Special Features-CB. */
+	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+		ha->sf_init_cb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
+						&ha->sf_init_cb_dma);
+		if (!ha->sf_init_cb)
+			goto fail_sf_init_cb;
+		ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0199,
+			   "sf_init_cb=%p.\n", ha->sf_init_cb);
+	}
+
 	INIT_LIST_HEAD(&ha->gbl_dsd_list);
 
 	/* Get consistent memory allocated for Async Port-Database. */
@@ -4273,6 +4283,8 @@  qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
 fail_loop_id_map:
 	dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma);
 fail_async_pd:
+	dma_pool_free(ha->s_dma_pool, ha->sf_init_cb, ha->sf_init_cb_dma);
+fail_sf_init_cb:
 	dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma);
 fail_ex_init_cb:
 	kfree(ha->npiv_info);
@@ -4695,6 +4707,10 @@  qla2x00_mem_free(struct qla_hw_data *ha)
 	ha->ms_iocb = NULL;
 	ha->ms_iocb_dma = 0;
 
+	if (ha->sf_init_cb)
+		dma_pool_free(ha->s_dma_pool,
+			      ha->sf_init_cb, ha->sf_init_cb_dma);
+
 	if (ha->ex_init_cb)
 		dma_pool_free(ha->s_dma_pool,
 			ha->ex_init_cb, ha->ex_init_cb_dma);
@@ -4782,6 +4798,8 @@  qla2x00_mem_free(struct qla_hw_data *ha)
 	kfree(ha->swl);
 	ha->swl = NULL;
 	kfree(ha->loop_id_map);
+	ha->sf_init_cb = NULL;
+	ha->sf_init_cb_dma = 0;
 	ha->loop_id_map = NULL;
 }