Thread (21 messages) 21 messages, 3 authors, 2021-01-08

RE: [EXT] Re: [PATCH v3 1/7] qla2xxx: Implementation to get and manage host, target stats and initiator port

From: Saurav Kashyap <skashyap@marvell.com>
Date: 2021-01-07 05:35:16

Hi Himanshu,
Thanks for a review, comments inline
-----Original Message-----
From: Himanshu Madhani <redacted>
Sent: Wednesday, January 6, 2021 8:35 PM
To: Nilesh Javali <njavali@marvell.com>; Saurav Kashyap
[off-list ref]
Cc: Martin K. Petersen <martin.petersen@oracle.com>; linux-
scsi@vger.kernel.org; GR-QLogic-Storage-Upstream <GR-QLogic-Storage-
Upstream@marvell.com>
Subject: [EXT] Re: [PATCH v3 1/7] qla2xxx: Implementation to get and manage
host, target stats and initiator port

External Email

----------------------------------------------------------------------
Hi Saurav,
quoted
On Jan 5, 2021, at 4:38 AM, Nilesh Javali [off-list ref] wrote:

From: Saurav Kashyap <skashyap@marvell.com>

This statistics will help in debugging process and checking specific
error counts.
An example of sample statistics output would be nice (not must)
quoted
Signed-off-by: Saurav Kashyap <skashyap@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Reported-by: kernel test robot <redacted>
---
drivers/scsi/qla2xxx/qla_attr.c |   9 +
drivers/scsi/qla2xxx/qla_bsg.c  | 342 ++++++++++++++++++++++++++++++++
drivers/scsi/qla2xxx/qla_bsg.h  |   5 +
drivers/scsi/qla2xxx/qla_def.h  |  71 +++++++
drivers/scsi/qla2xxx/qla_gbl.h  |  23 +++
drivers/scsi/qla2xxx/qla_gs.c   |   1 +
drivers/scsi/qla2xxx/qla_init.c | 216 ++++++++++++++++++++
drivers/scsi/qla2xxx/qla_isr.c  |  22 ++
drivers/scsi/qla2xxx/qla_mbx.c  |   9 +
drivers/scsi/qla2xxx/qla_os.c   |  20 ++
10 files changed, 718 insertions(+)
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index ab45ac1e5a72..63391c9be05d 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -710,6 +710,12 @@ qla2x00_sysfs_write_reset(struct file *filp, struct
kobject *kobj,
quoted
		ql_log(ql_log_info, vha, 0x706e,
		    "Issuing ISP reset.\n");

+		if (vha->hw->flags.port_isolated) {
+			ql_log(ql_log_info, vha, 0x706e,
+			       "Port is isolated, returning.\n");
+			return -EINVAL;
+		}
+
		scsi_block_requests(vha->host);
		if (IS_QLA82XX(ha)) {
			ha->flags.isp82xx_no_md_cap = 1;
@@ -2717,6 +2723,9 @@ qla2x00_issue_lip(struct Scsi_Host *shost)
	if (IS_QLAFX00(vha->hw))
		return 0;

+	if (vha->hw->flags.port_isolated)
+		return 0;
+
	qla2x00_loop_reset(vha);
	return 0;
}
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 23b604832a54..ae3bc6ec8722 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -4,6 +4,7 @@
 * Copyright (c)  2003-2014 QLogic Corporation
 */
#include "qla_def.h"
+#include "qla_gbl.h"

#include <linux/kthread.h>
#include <linux/vmalloc.h>
@@ -2444,6 +2445,323 @@ qla2x00_get_flash_image_status(struct bsg_job
*bsg_job)
quoted
	return 0;
}

+static int
+qla2x00_manage_host_stats(struct bsg_job *bsg_job)
+{
+	scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct ql_vnd_mng_host_stats_param *req_data;
+	struct ql_vnd_mng_host_stats_resp rsp_data;
+	u32 req_data_len;
+	int ret = 0;
+
+	if (!vha->flags.online) {
+		ql_log(ql_log_warn, vha, 0x0000, "Host is not online.\n");
+		return -EIO;
+	}
+
+	req_data_len = bsg_job->request_payload.payload_len;
+
+	if (req_data_len != sizeof(struct ql_vnd_mng_host_stats_param)) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data_len invalid.\n");
+		return -EIO;
+	}
+
+	req_data = kzalloc(sizeof(*req_data), GFP_KERNEL);
+	if (!req_data) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data memory allocation
failure.\n");
quoted
+		return -ENOMEM;
+	}
+
+	/* Copy the request buffer in req_data */
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+			  bsg_job->request_payload.sg_cnt, req_data,
+			  req_data_len);
+
+	switch (req_data->action) {
+	case 0:
+		ret = qla2xxx_stop_stats(vha->host, req_data->stat_type);
+		break;
+	case 1:
+		ret = qla2xxx_start_stats(vha->host, req_data->stat_type);
+		break;
+	case 2:
+		ret = qla2xxx_reset_stats(vha->host, req_data->stat_type);
+		break;
+	default:
+		ql_log(ql_log_warn, vha, 0x0000, "Invalid action.\n");
+		ret = -EIO;
+		break;
+	}
+
I don’t like using case 0/1/2 for the above action switch statements. Why not
define it as QL_STATS_ACTION_START/STOP/RESET.
<SK> Make sense.
quoted
+	kfree(req_data);
+
+	/* Prepare response */
+	rsp_data.status = ret;
+	bsg_job->reply_payload.payload_len = sizeof(struct
ql_vnd_mng_host_stats_resp);
quoted
+
+	bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_OK;
+	bsg_reply->reply_payload_rcv_len =
+		sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+				    bsg_job->reply_payload.sg_cnt,
+				    &rsp_data,
+				    sizeof(struct ql_vnd_mng_host_stats_resp));
+
+	bsg_reply->result = DID_OK;
+	bsg_job_done(bsg_job, bsg_reply->result,
+		     bsg_reply->reply_payload_rcv_len);
+
+	return ret;
+}
+
+static int
+qla2x00_get_host_stats(struct bsg_job *bsg_job)
+{
+	scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct ql_vnd_stats_param *req_data;
+	struct ql_vnd_host_stats_resp rsp_data;
+	u32 req_data_len;
+	int ret = 0;
+	u64 ini_entry_count = 0;
+	u64 entry_count = 0;
+	u64 tgt_num = 0;
+	u64 tmp_stat_type = 0;
+	u64 response_len = 0;
+	void *data;
+
+	req_data_len = bsg_job->request_payload.payload_len;
+
+	if (req_data_len != sizeof(struct ql_vnd_stats_param)) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data_len invalid.\n");
+		return -EIO;
+	}
+
+	req_data = kzalloc(sizeof(*req_data), GFP_KERNEL);
+	if (!req_data) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data memory allocation
failure.\n");
quoted
+		return -ENOMEM;
+	}
+
+	/* Copy the request buffer in req_data */
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+			  bsg_job->request_payload.sg_cnt, req_data,
req_data_len);
quoted
+
+	/* Copy stat type to work on it */
+	tmp_stat_type = req_data->stat_type;
+
+	if (tmp_stat_type & QLA2XX_TGT_SHT_LNK_DOWN) {
+		/* Num of tgts connected to this host */
+		tgt_num = qla2x00_get_num_tgts(vha);
+		/* unset BIT_17 */
+		tmp_stat_type &= ~(1 << 17);
+	}
+
+	/* Total ini stats */
+	ini_entry_count = qla2x00_count_set_bits(tmp_stat_type);
+
+	/* Total number of entries */
+	entry_count = ini_entry_count + tgt_num;
+
+	response_len = sizeof(struct ql_vnd_host_stats_resp) +
+		(sizeof(struct ql_vnd_stat_entry) * entry_count);
+
+	if (response_len > bsg_job->reply_payload.payload_len) {
+		rsp_data.status = EXT_STATUS_BUFFER_TOO_SMALL;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
EXT_STATUS_BUFFER_TOO_SMALL;
quoted
+		bsg_job->reply_payload.payload_len = sizeof(struct
ql_vnd_mng_host_stats_resp);
quoted
+
+		bsg_reply->reply_payload_rcv_len =
+			sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+					    bsg_job->reply_payload.sg_cnt,
&rsp_data,
quoted
+					    sizeof(struct
ql_vnd_mng_host_stats_resp));
quoted
+
+		bsg_reply->result = DID_OK;
+		bsg_job_done(bsg_job, bsg_reply->result,
+			     bsg_reply->reply_payload_rcv_len);
+		goto host_stat_out;
+	}
+
+	data = kzalloc(response_len, GFP_KERNEL);
+
+	ret = qla2xxx_get_ini_stats(fc_bsg_to_shost(bsg_job), req_data-
stat_type,
+				    data, response_len);
+
+	rsp_data.status = EXT_STATUS_OK;
+	bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_OK;
+
+	bsg_reply->reply_payload_rcv_len = sg_copy_from_buffer(bsg_job-
reply_payload.sg_list,
+							       bsg_job-
reply_payload.sg_cnt,
+							       data,
response_len);
quoted
+	bsg_reply->result = DID_OK;
+	bsg_job_done(bsg_job, bsg_reply->result,
+		     bsg_reply->reply_payload_rcv_len);
+
+	kfree(data);
+host_stat_out:
+	kfree(req_data);
+	return ret;
+}
+
+static struct fc_rport *
+qla2xxx_find_rport(scsi_qla_host_t *vha, uint32_t tgt_num)
+{
+	fc_port_t *fcport = NULL;
+
+	list_for_each_entry(fcport, &vha->vp_fcports, list) {
+		if (fcport->rport->number == tgt_num)
+			return fcport->rport;
+	}
+	return NULL;
+}
+
+static int
+qla2x00_get_tgt_stats(struct bsg_job *bsg_job)
+{
+	scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct ql_vnd_tgt_stats_param *req_data;
+	u32 req_data_len;
+	int ret = 0;
+	u64 response_len = 0;
+	struct ql_vnd_tgt_stats_resp *data = NULL;
+	struct fc_rport *rport = NULL;
+
+	if (!vha->flags.online) {
+		ql_log(ql_log_warn, vha, 0x0000, "Host is not online.\n");
+		return -EIO;
+	}
+
+	req_data_len = bsg_job->request_payload.payload_len;
+
+	if (req_data_len != sizeof(struct ql_vnd_stat_entry)) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data_len invalid.\n");
+		return -EIO;
+	}
+
+	req_data = kzalloc(sizeof(*req_data), GFP_KERNEL);
+	if (!req_data) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data memory allocation
failure.\n");
quoted
+		return -ENOMEM;
+	}
+
+	/* Copy the request buffer in req_data */
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+			  bsg_job->request_payload.sg_cnt,
+			  req_data, req_data_len);
+
+	response_len = sizeof(struct ql_vnd_tgt_stats_resp) +
+		sizeof(struct ql_vnd_stat_entry);
+
+	/* structure + size for one entry */
+	data = kzalloc(response_len, GFP_KERNEL);
+	if (!data) {
+		kfree(req_data);
+		return -ENOMEM;
+	}
+
+	if (response_len > bsg_job->reply_payload.payload_len) {
+		data->status = EXT_STATUS_BUFFER_TOO_SMALL;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
EXT_STATUS_BUFFER_TOO_SMALL;
quoted
+		bsg_job->reply_payload.payload_len = sizeof(struct
ql_vnd_mng_host_stats_resp);
quoted
+
+		bsg_reply->reply_payload_rcv_len =
+			sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+					    bsg_job->reply_payload.sg_cnt,
&data,
quoted
+					    sizeof(struct
ql_vnd_tgt_stats_resp));
quoted
+
+		bsg_reply->result = DID_OK;
+		bsg_job_done(bsg_job, bsg_reply->result,
+			     bsg_reply->reply_payload_rcv_len);
+		goto tgt_stat_out;
+	}
+
+	rport = qla2xxx_find_rport(vha, req_data->tgt_id);
+	if (!rport) {
+		ql_log(ql_log_warn, vha, 0x0000, "target %d not found.\n",
req_data->tgt_id);
quoted
+		ret = EXT_STATUS_INVALID_PARAM;
+		data->status = EXT_STATUS_INVALID_PARAM;
+		goto reply;
+	}
+
+	ret = qla2xxx_get_tgt_stats(fc_bsg_to_shost(bsg_job), req_data-
stat_type,
+				    rport, (void *)data, response_len);
+
+	bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_OK;
+reply:
+	bsg_reply->reply_payload_rcv_len =
+		sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+				    bsg_job->reply_payload.sg_cnt, data,
+				    response_len);
+	bsg_reply->result = DID_OK;
+	bsg_job_done(bsg_job, bsg_reply->result,
+		     bsg_reply->reply_payload_rcv_len);
+
+tgt_stat_out:
+	kfree(data);
+	kfree(req_data);
+
+	return ret;
+}
+
+static int
+qla2x00_manage_host_port(struct bsg_job *bsg_job)
+{
+	scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct ql_vnd_mng_host_port_param *req_data;
+	struct ql_vnd_mng_host_port_resp rsp_data;
+	u32 req_data_len;
+	int ret = 0;
+
+	req_data_len = bsg_job->request_payload.payload_len;
+
+	if (req_data_len != sizeof(struct ql_vnd_mng_host_port_param)) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data_len invalid.\n");
+		return -EIO;
+	}
+
+	req_data = kzalloc(sizeof(*req_data), GFP_KERNEL);
+	if (!req_data) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data memory allocation
failure.\n");
quoted
+		return -ENOMEM;
+	}
+
+	/* Copy the request buffer in req_data */
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+			  bsg_job->request_payload.sg_cnt, req_data,
req_data_len);
quoted
+
+	switch (req_data->action) {
+	case 0:
+		ret = qla2xxx_enable_port(vha->host);
+		break;
+	case 1:
+		ret = qla2xxx_disable_port(vha->host);
+		break;
+	default:
+		ql_log(ql_log_warn, vha, 0x0000, "Invalid action.\n");
+		ret = -EIO;
+		break;
+	}
+
Same comment as above… why not use more descriptive name so that user
does not have to go to header file to understand what is case 0 and case 1.. Just
use QL_HOST_ACTION_ENABLE/DISABLE
<SK> Make sense.
quoted
+	kfree(req_data);
+
+	/* Prepare response */
+	rsp_data.status = ret;
+	bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_OK;
+	bsg_job->reply_payload.payload_len = sizeof(struct
ql_vnd_mng_host_port_resp);
quoted
+
+	bsg_reply->reply_payload_rcv_len =
+		sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+				    bsg_job->reply_payload.sg_cnt, &rsp_data,
+				    sizeof(struct ql_vnd_mng_host_port_resp));
+	bsg_reply->result = DID_OK;
+	bsg_job_done(bsg_job, bsg_reply->result,
+		     bsg_reply->reply_payload_rcv_len);
+
+	return ret;
+}
+
static int
qla2x00_process_vendor_specific(struct bsg_job *bsg_job)
{
@@ -2520,6 +2838,18 @@ qla2x00_process_vendor_specific(struct bsg_job
*bsg_job)
quoted
	case QL_VND_SS_GET_FLASH_IMAGE_STATUS:
		return qla2x00_get_flash_image_status(bsg_job);

+	case QL_VND_MANAGE_HOST_STATS:
+		return qla2x00_manage_host_stats(bsg_job);
+
+	case QL_VND_GET_HOST_STATS:
+		return qla2x00_get_host_stats(bsg_job);
+
+	case QL_VND_GET_TGT_STATS:
+		return qla2x00_get_tgt_stats(bsg_job);
+
+	case QL_VND_MANAGE_HOST_PORT:
+		return qla2x00_manage_host_port(bsg_job);
+
	default:
		return -ENOSYS;
	}
@@ -2547,6 +2877,17 @@ qla24xx_bsg_request(struct bsg_job *bsg_job)
		vha = shost_priv(host);
	}

+	/* Disable port will bring down the chip, allow enable command */
+	if (bsg_request->rqst_data.h_vendor.vendor_cmd[0] ==
QL_VND_MANAGE_HOST_PORT ||
quoted
+	    bsg_request->rqst_data.h_vendor.vendor_cmd[0] ==
QL_VND_GET_HOST_STATS)
quoted
+		goto skip_chip_chk;
+
+	if (vha->hw->flags.port_isolated) {
+		bsg_reply->result = DID_ERROR;
+		/* operation not permitted */
+		return -EPERM;
+	}
+
	if (qla2x00_chip_is_down(vha)) {
		ql_dbg(ql_dbg_user, vha, 0x709f,
		    "BSG: ISP abort active/needed -- cmd=%d.\n",
@@ -2554,6 +2895,7 @@ qla24xx_bsg_request(struct bsg_job *bsg_job)
		return -EBUSY;
	}

+skip_chip_chk:
	ql_dbg(ql_dbg_user, vha, 0x7000,
	    "Entered %s msgcode=0x%x.\n", __func__, bsg_request->msgcode);
diff --git a/drivers/scsi/qla2xxx/qla_bsg.h b/drivers/scsi/qla2xxx/qla_bsg.h
index 1a09b5512267..0274e99e4a12 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.h
+++ b/drivers/scsi/qla2xxx/qla_bsg.h
@@ -31,6 +31,10 @@
#define QL_VND_DPORT_DIAGNOSTICS	0x19
#define QL_VND_GET_PRIV_STATS_EX	0x1A
#define QL_VND_SS_GET_FLASH_IMAGE_STATUS	0x1E
+#define QL_VND_MANAGE_HOST_STATS	0x23
+#define QL_VND_GET_HOST_STATS		0x24
+#define QL_VND_GET_TGT_STATS		0x25
+#define QL_VND_MANAGE_HOST_PORT		0x26

/* BSG Vendor specific subcode returns */
#define EXT_STATUS_OK			0
@@ -40,6 +44,7 @@
#define EXT_STATUS_DATA_OVERRUN		7
#define EXT_STATUS_DATA_UNDERRUN	8
#define EXT_STATUS_MAILBOX		11
+#define EXT_STATUS_BUFFER_TOO_SMALL	16
#define EXT_STATUS_NO_MEMORY		17
#define EXT_STATUS_DEVICE_OFFLINE	22
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 30c7e5e63851..f2f1b0231033 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -2557,6 +2557,10 @@ typedef struct fc_port {
	u16 n2n_chip_reset;

	struct dentry *dfs_rport_dir;
+
+	u64 tgt_short_link_down_cnt;
+	u64 tgt_link_down_time;
+	u64 dev_loss_tmo;
} fc_port_t;

enum {
@@ -3922,6 +3926,7 @@ struct qla_hw_data {
		uint32_t	scm_enabled:1;
		uint32_t	max_req_queue_warned:1;
		uint32_t	plogi_template_valid:1;
+		uint32_t	port_isolated:1;
Just for my understanding, in regards to stats display, What does port_isolated
indicates?
<SK> It's a state of the port, it comes as part of managing the port. If the error count is more,
user can opt to change the state and port will go to offline state.
quoted
	} flags;

	uint16_t max_exchg;
@@ -4851,6 +4856,13 @@ typedef struct scsi_qla_host {
	uint8_t	scm_fabric_connection_flags;

	unsigned int irq_offset;
+
+	u64 hw_err_cnt;
+	u64 interface_err_cnt;
+	u64 cmd_timeout_cnt;
+	u64 reset_cmd_err_cnt;
+	u64 link_down_time;
+	u64 short_link_down_cnt;
} scsi_qla_host_t;

struct qla27xx_image_status {
@@ -5174,6 +5186,65 @@ struct sff_8247_a0 {
#define PRLI_PHASE(_cls) \
	((_cls == DSC_LS_PRLI_PEND) || (_cls == DSC_LS_PRLI_COMP))

+enum ql_vnd_host_stat_action {
+	stop = 0,
+	start,
+	clear,
+};
+
+struct ql_vnd_mng_host_stats_param {
+	u32 stat_type;
+	enum ql_vnd_host_stat_action action;
+} __packed;
+
+struct ql_vnd_mng_host_stats_resp {
+	u32 status;
+} __packed;
+
+struct ql_vnd_stats_param {
+	u32 stat_type;
+} __packed;
+
+struct ql_vnd_tgt_stats_param {
+	s32 tgt_id;
+	u32 stat_type;
+} __packed;
+
+enum ql_vnd_host_port_action {
+	enable = 0,
+	disable,
+};
+
+struct ql_vnd_mng_host_port_param {
+	enum ql_vnd_host_port_action action;
+} __packed;
+
+struct ql_vnd_mng_host_port_resp {
+	u32 status;
+} __packed;
+
+struct ql_vnd_stat_entry {
+	u32 stat_type;	/* Failure type */
+	u32 tgt_num;	/* Target Num */
+	u64 cnt;	/* Counter value */
+} __packed;
+
+struct ql_vnd_stats {
+	u64 entry_count; /* Num of entries */
+	u64 rservd;
+	struct ql_vnd_stat_entry entry[0]; /* Place holder of entries */
+} __packed;
+
+struct ql_vnd_host_stats_resp {
+	u32 status;
+	struct ql_vnd_stats stats;
+} __packed;
+
+struct ql_vnd_tgt_stats_resp {
+	u32 status;
+	struct ql_vnd_stats stats;
+} __packed;
+
#include "qla_target.h"
#include "qla_gbl.h"
#include "qla_dbg.h"
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index e39b4f2da73a..708f82311b83 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -945,4 +945,27 @@ extern void
qla2x00_dfs_remove_rport(scsi_qla_host_t *vha, struct fc_port *fp);
quoted
/* nvme.c */
void qla_nvme_unregister_remote_port(struct fc_port *fcport);
void qla_handle_els_plogi_done(scsi_qla_host_t *vha, struct event_arg *ea);
+
+#define QLA2XX_HW_ERROR			BIT_0
+#define QLA2XX_SHT_LNK_DWN		BIT_1
+#define QLA2XX_INT_ERR			BIT_2
+#define QLA2XX_CMD_TIMEOUT		BIT_3
+#define QLA2XX_RESET_CMD_ERR		BIT_4
+#define QLA2XX_TGT_SHT_LNK_DOWN		BIT_17
+
+#define QLA2XX_MAX_LINK_DOWN_TIME	100
+
+int qla2xxx_start_stats(struct Scsi_Host *shost, u32 flags);
+int qla2xxx_stop_stats(struct Scsi_Host *shost, u32 flags);
+int qla2xxx_reset_stats(struct Scsi_Host *shost, u32 flags);
+
+int qla2xxx_get_ini_stats(struct Scsi_Host *shost, u32 flags, void *data, u64
size);
quoted
+int qla2xxx_get_tgt_stats(struct Scsi_Host *shost, u32 flags,
+			  struct fc_rport *rport, void *data, u64 size);
+int qla2xxx_disable_port(struct Scsi_Host *shost);
+int qla2xxx_enable_port(struct Scsi_Host *shost);
+
+uint64_t qla2x00_get_num_tgts(scsi_qla_host_t *vha);
+uint64_t qla2x00_count_set_bits(u32 num);
+
#endif /* _QLA_GBL_H */
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index 391ac75e3de3..517d358b0031 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -3563,6 +3563,7 @@ void qla24xx_async_gnnft_done(scsi_qla_host_t
*vha, srb_t *sp)
quoted
					       __func__, __LINE__,
					       fcport->port_name);

+					fcport->tgt_link_down_time = 0;
	qlt_schedule_sess_for_deletion(fcport);
quoted
					continue;
				}
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index dcc0f0d823db..410ff5534a59 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -4993,6 +4993,9 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t
flags)
quoted
	fcport->login_retry = vha->hw->login_retry_count;
	fcport->chip_reset = vha->hw->base_qpair->chip_reset;
	fcport->logout_on_delete = 1;
+	fcport->tgt_link_down_time = QLA2XX_MAX_LINK_DOWN_TIME;
+	fcport->tgt_short_link_down_cnt = 0;
+	fcport->dev_loss_tmo = 0;

	if (!fcport->ct_desc.ct_sns) {
		ql_log(ql_log_warn, vha, 0xd049,
@@ -5490,6 +5493,7 @@ qla2x00_reg_remote_port(scsi_qla_host_t *vha,
fc_port_t *fcport)
quoted
	spin_lock_irqsave(fcport->vha->host->host_lock, flags);
	*((fc_port_t **)rport->dd_data) = fcport;
	spin_unlock_irqrestore(fcport->vha->host->host_lock, flags);
+	fcport->dev_loss_tmo = rport->dev_loss_tmo;

	rport->supported_classes = fcport->supported_classes;
@@ -5548,6 +5552,11 @@ qla2x00_update_fcport(scsi_qla_host_t *vha,
fc_port_t *fcport)
quoted
		fcport->logout_on_delete = 1;
	fcport->n2n_chip_reset = fcport->n2n_link_reset_cnt = 0;

+	if (fcport->tgt_link_down_time < fcport->dev_loss_tmo) {
+		fcport->tgt_short_link_down_cnt++;
+		fcport->tgt_link_down_time =
QLA2XX_MAX_LINK_DOWN_TIME;
quoted
+	}
+
	switch (vha->hw->current_topology) {
	case ISP_CFG_N:
	case ISP_CFG_NL:
@@ -6908,6 +6917,9 @@ qla2x00_abort_isp(scsi_qla_host_t *vha)
	if (vha->flags.online) {
		qla2x00_abort_isp_cleanup(vha);

+		if (vha->hw->flags.port_isolated)
+			return status;
+
		if (test_and_clear_bit(ISP_ABORT_TO_ROM, &vha-
dpc_flags)) {
			ha->flags.chip_reset_done = 1;
			vha->flags.online = 1;
@@ -7029,6 +7041,11 @@ qla2x00_abort_isp(scsi_qla_host_t *vha)
	}

+	if (vha->hw->flags.port_isolated) {
+		qla2x00_abort_isp_cleanup(vha);
+		return status;
+	}
+
	if (!status) {
		ql_dbg(ql_dbg_taskm, vha, 0x8022, "%s succeeded.\n",
__func__);
quoted
		qla2x00_configure_hba(vha);
@@ -9171,3 +9188,202 @@ int qla2xxx_delete_qpair(struct scsi_qla_host
*vha, struct qla_qpair *qpair)
quoted
fail:
	return ret;
}
+
+uint64_t
+qla2x00_count_set_bits(uint32_t num)
+{
+	/* Brian Kernighan's Alogorithm */
+	u64 count = 0;
+
+	while (num) {
+		num &= (num - 1);
+		count++;
+	}
+	return count;
+}
+
+uint64_t
+qla2x00_get_num_tgts(scsi_qla_host_t *vha)
+{
+	fc_port_t *f, *tf;
+	u64 count = 0;
+
+	f = NULL;
+	tf = NULL;
+
+	list_for_each_entry_safe(f, tf, &vha->vp_fcports, list) {
+		if (f->port_type != FCT_TARGET)
+			continue;
+		count++;
+	}
+	return count;
+}
+
+int qla2xxx_reset_stats(struct Scsi_Host *host, u32 flags)
+{
+	scsi_qla_host_t *vha = shost_priv(host);
+	fc_port_t *fcport = NULL;
+	unsigned long int_flags;
+
+	if (flags & QLA2XX_HW_ERROR)
+		vha->hw_err_cnt = 0;
+	if (flags & QLA2XX_SHT_LNK_DWN)
+		vha->short_link_down_cnt = 0;
+	if (flags & QLA2XX_INT_ERR)
+		vha->interface_err_cnt = 0;
+	if (flags & QLA2XX_CMD_TIMEOUT)
+		vha->cmd_timeout_cnt = 0;
+	if (flags & QLA2XX_RESET_CMD_ERR)
+		vha->reset_cmd_err_cnt = 0;
+	if (flags & QLA2XX_TGT_SHT_LNK_DOWN) {
+		spin_lock_irqsave(&vha->hw->tgt.sess_lock, int_flags);
+		list_for_each_entry(fcport, &vha->vp_fcports, list) {
+			fcport->tgt_short_link_down_cnt = 0;
+			fcport->tgt_link_down_time =
QLA2XX_MAX_LINK_DOWN_TIME;
quoted
+		}
+		spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, int_flags);
+	}
+	vha->link_down_time = QLA2XX_MAX_LINK_DOWN_TIME;
+	return 0;
+}
+
+int qla2xxx_start_stats(struct Scsi_Host *host, u32 flags)
+{
+	return qla2xxx_reset_stats(host, flags);
+}
+
+int qla2xxx_stop_stats(struct Scsi_Host *host, u32 flags)
+{
+	return qla2xxx_reset_stats(host, flags);
+}
+
+int qla2xxx_get_ini_stats(struct Scsi_Host *host, u32 flags,
+			  void *data, u64 size)
+{
+	scsi_qla_host_t *vha = shost_priv(host);
+	struct ql_vnd_host_stats_resp *resp = (struct ql_vnd_host_stats_resp
*)data;
quoted
+	struct ql_vnd_stats *rsp_data = &resp->stats;
+	u64 ini_entry_count = 0;
+	u64 i = 0;
+	u64 entry_count = 0;
+	u64 num_tgt = 0;
+	u32 tmp_stat_type = 0;
+	fc_port_t *fcport = NULL;
+	unsigned long int_flags;
+
+	/* Copy stat type to work on it */
+	tmp_stat_type = flags;
+
+	if (tmp_stat_type & BIT_17) {
+		num_tgt = qla2x00_get_num_tgts(vha);
+		/* unset BIT_17 */
+		tmp_stat_type &= ~(1 << 17);
+	}
+	ini_entry_count = qla2x00_count_set_bits(tmp_stat_type);
+
+	entry_count = ini_entry_count + num_tgt;
+
+	rsp_data->entry_count = entry_count;
+
+	i = 0;
+	if (flags & QLA2XX_HW_ERROR) {
+		rsp_data->entry[i].stat_type = QLA2XX_HW_ERROR;
+		rsp_data->entry[i].tgt_num = 0x0;
+		rsp_data->entry[i].cnt = vha->hw_err_cnt;
+		i++;
+	}
+
+	if (flags & QLA2XX_SHT_LNK_DWN) {
+		rsp_data->entry[i].stat_type = QLA2XX_SHT_LNK_DWN;
+		rsp_data->entry[i].tgt_num = 0x0;
+		rsp_data->entry[i].cnt = vha->short_link_down_cnt;
+		i++;
+	}
+
+	if (flags & QLA2XX_INT_ERR) {
+		rsp_data->entry[i].stat_type = QLA2XX_INT_ERR;
+		rsp_data->entry[i].tgt_num = 0x0;
+		rsp_data->entry[i].cnt = vha->interface_err_cnt;
+		i++;
+	}
+
+	if (flags & QLA2XX_CMD_TIMEOUT) {
+		rsp_data->entry[i].stat_type = QLA2XX_CMD_TIMEOUT;
+		rsp_data->entry[i].tgt_num = 0x0;
+		rsp_data->entry[i].cnt = vha->cmd_timeout_cnt;
+		i++;
+	}
+
+	if (flags & QLA2XX_RESET_CMD_ERR) {
+		rsp_data->entry[i].stat_type = QLA2XX_RESET_CMD_ERR;
+		rsp_data->entry[i].tgt_num = 0x0;
+		rsp_data->entry[i].cnt = vha->reset_cmd_err_cnt;
+		i++;
+	}
+
+	/* i will continue from previous loop, as target
+	 * entries are after initiator
+	 */
+	if (flags & QLA2XX_TGT_SHT_LNK_DOWN) {
+		spin_lock_irqsave(&vha->hw->tgt.sess_lock, int_flags);
+		list_for_each_entry(fcport, &vha->vp_fcports, list) {
+			if (fcport->port_type != FCT_TARGET)
+				continue;
+			if (!fcport->rport)
+				continue;
+			rsp_data->entry[i].stat_type =
QLA2XX_TGT_SHT_LNK_DOWN;
quoted
+			rsp_data->entry[i].tgt_num = fcport->rport->number;
+			rsp_data->entry[i].cnt = fcport-
tgt_short_link_down_cnt;
+			i++;
+		}
+		spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, int_flags);
+	}
+	resp->status = EXT_STATUS_OK;
+
+	return 0;
+}
+
+int qla2xxx_get_tgt_stats(struct Scsi_Host *host, u32 flags,
+			  struct fc_rport *rport, void *data, u64 size)
+{
+	struct ql_vnd_tgt_stats_resp *tgt_data = data;
+	fc_port_t *fcport = *(fc_port_t **)rport->dd_data;
+
+	tgt_data->status = 0;
+	tgt_data->stats.entry_count = 1;
+	tgt_data->stats.entry[0].stat_type = flags;
+	tgt_data->stats.entry[0].tgt_num = rport->number;
+	tgt_data->stats.entry[0].cnt = fcport->tgt_short_link_down_cnt;
+
+	return 0;
+}
+
+int qla2xxx_disable_port(struct Scsi_Host *host)
+{
+	scsi_qla_host_t *vha = shost_priv(host);
+
+	vha->hw->flags.port_isolated = 1;
+
+	if (qla2x00_chip_is_down(vha))
+		return 0;
+
+	if (vha->flags.online) {
+		qla2x00_abort_isp_cleanup(vha);
+		qla2x00_wait_for_sess_deletion(vha);
+	}
+
+	return 0;
+}
+
+int qla2xxx_enable_port(struct Scsi_Host *host)
+{
+	scsi_qla_host_t *vha = shost_priv(host);
+
+	vha->hw->flags.port_isolated = 0;
+	/* Set the flag to 1, so that isp_abort can proceed */
+	vha->flags.online = 1;
+	set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+	qla2xxx_wake_dpc(vha);
+
+	return 0;
+}
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index f9142dbec112..9cf8326ab9fc 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -1059,6 +1059,9 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct
rsp_que *rsp, uint16_t *mb)
quoted
	case MBA_SYSTEM_ERR:		/* System Error */
		mbx = 0;
+
+		vha->hw_err_cnt++;
+
		if (IS_QLA81XX(ha) || IS_QLA83XX(ha) ||
		    IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
			u16 m[4];
@@ -1112,6 +1115,8 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct
rsp_que *rsp, uint16_t *mb)
quoted
		ql_log(ql_log_warn, vha, 0x5006,
		    "ISP Request Transfer Error (%x).\n",  mb[1]);

+		vha->hw_err_cnt++;
+
		set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
		break;
@@ -1119,6 +1124,8 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct
rsp_que *rsp, uint16_t *mb)
quoted
		ql_log(ql_log_warn, vha, 0x5007,
		    "ISP Response Transfer Error (%x).\n", mb[1]);

+		vha->hw_err_cnt++;
+
		set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
		break;
@@ -1176,12 +1183,18 @@ qla2x00_async_event(scsi_qla_host_t *vha,
struct rsp_que *rsp, uint16_t *mb)
quoted
		vha->flags.management_server_logged_in = 0;
		qla2x00_post_aen_work(vha, FCH_EVT_LINKUP, ha-
link_data_rate);

+		if (vha->link_down_time < vha->hw->port_down_retry_count) {
+			vha->short_link_down_cnt++;
+			vha->link_down_time =
QLA2XX_MAX_LINK_DOWN_TIME;
quoted
+		}
+
		break;

	case MBA_LOOP_DOWN:		/* Loop Down Event */
		SAVE_TOPO(ha);
		ha->flags.lip_ae = 0;
		ha->current_topology = 0;
+		vha->link_down_time = 0;

		mbx = (IS_QLA81XX(ha) || IS_QLA8031(ha))
			? rd_reg_word(&reg24->mailbox4) : 0;
@@ -1503,6 +1516,7 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct
rsp_que *rsp, uint16_t *mb)
quoted
		ql_dbg(ql_dbg_async, vha, 0x5016,
		    "Discard RND Frame -- %04x %04x %04x.\n",
		    mb[1], mb[2], mb[3]);
+		vha->interface_err_cnt++;
		break;

	case MBA_TRACE_NOTIFICATION:
@@ -1592,6 +1606,7 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct
rsp_que *rsp, uint16_t *mb)
quoted
	case MBA_IDC_AEN:
		if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+			vha->hw_err_cnt++;
			qla27xx_handle_8200_aen(vha, mb);
		} else if (IS_QLA83XX(ha)) {
			mb[4] = rd_reg_word(&reg24->mailbox4);
@@ -3101,6 +3116,8 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct
rsp_que *rsp, void *pkt)
quoted
				    "Dropped frame(s) detected (0x%x of 0x%x
bytes).\n",
quoted
				    resid, scsi_bufflen(cp));

+				vha->interface_err_cnt++;
+
				res = DID_ERROR << 16 | lscsi_status;
				goto check_scsi_status;
			}
@@ -3126,6 +3143,8 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct
rsp_que *rsp, void *pkt)
quoted
			    "Dropped frame(s) detected (0x%x of 0x%x
bytes).\n",
quoted
			    resid, scsi_bufflen(cp));

+			vha->interface_err_cnt++;
+
			res = DID_ERROR << 16 | lscsi_status;
			goto check_scsi_status;
		} else {
@@ -3208,6 +3227,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct
rsp_que *rsp, void *pkt)
quoted
	case CS_TRANSPORT:
		res = DID_ERROR << 16;
+		vha->hw_err_cnt++;

		if (!IS_PI_SPLIT_DET_CAPABLE(ha))
			break;
@@ -3228,6 +3248,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct
rsp_que *rsp, void *pkt)
quoted
		ql_dump_buffer(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe0ee,
		    pkt, sizeof(*sts24));
		res = DID_ERROR << 16;
+		vha->hw_err_cnt++;
		break;
	default:
		res = DID_ERROR << 16;
@@ -3839,6 +3860,7 @@ qla24xx_msix_default(int irq, void *dev_id)
			    hccr);

			qla2xxx_check_risc_status(vha);
+			vha->hw_err_cnt++;

			ha->isp_ops->fw_dump(vha);
			set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index d7d4ab65009c..f438cdedca23 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -180,6 +180,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha,
mbx_cmd_t *mcp)
quoted
		ql_log(ql_log_warn, vha, 0xd035,
		    "Cmd access timeout, cmd=0x%x, Exiting.\n",
		    mcp->mb[0]);
+		vha->hw_err_cnt++;
		atomic_dec(&ha->num_pend_mbx_stage1);
		return QLA_FUNCTION_TIMEOUT;
	}
@@ -307,6 +308,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha,
mbx_cmd_t *mcp)
quoted
				atomic_dec(&ha->num_pend_mbx_stage2);
				ql_dbg(ql_dbg_mbx, vha, 0x1012,
				    "Pending mailbox timeout, exiting.\n");
+				vha->hw_err_cnt++;
				rval = QLA_FUNCTION_TIMEOUT;
				goto premature_exit;
			}
@@ -418,6 +420,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha,
mbx_cmd_t *mcp)
quoted
			    "mb[0-3]=[0x%x 0x%x 0x%x 0x%x] mb7 0x%x
host_status 0x%x hccr 0x%x\n",
quoted
			    command, ictrl, jiffies, mb[0], mb[1], mb[2], mb[3],
			    mb[7], host_status, hccr);
+			vha->hw_err_cnt++;

		} else {
			mb[0] = RD_MAILBOX_REG(ha, &reg->isp, 0);
@@ -425,6 +428,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha,
mbx_cmd_t *mcp)
quoted
			ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1119,
			    "MBX Command timeout for cmd %x, iocontrol=%x
jiffies=%lx "
quoted
			    "mb[0]=0x%x\n", command, ictrl, jiffies, mb[0]);
+			vha->hw_err_cnt++;
		}
		ql_dump_regs(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1019);
@@ -497,6 +501,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha,
mbx_cmd_t *mcp)
quoted
				    "mb[0]=0x%x, eeh_busy=0x%x. Scheduling
ISP "
quoted
				    "abort.\n", command, mcp->mb[0],
				    ha->flags.eeh_busy);
+				vha->hw_err_cnt++;
				set_bit(ISP_ABORT_NEEDED, &vha-
dpc_flags);
				qla2xxx_wake_dpc(vha);
			}
@@ -521,6 +526,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha,
mbx_cmd_t *mcp)
quoted
				    "Mailbox cmd timeout occurred, cmd=0x%x,
"
quoted
				    "mb[0]=0x%x. Scheduling ISP abort ",
				    command, mcp->mb[0]);
+				vha->hw_err_cnt++;
				set_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags);
				clear_bit(ISP_ABORT_NEEDED, &vha-
dpc_flags);
				/* Allow next mbx cmd to come in. */
@@ -625,6 +631,7 @@ qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t
req_dma, uint32_t risc_addr,
quoted
		ql_dbg(ql_dbg_mbx, vha, 0x1023,
		    "Failed=%x mb[0]=%x mb[1]=%x.\n",
		    rval, mcp->mb[0], mcp->mb[1]);
+		vha->hw_err_cnt++;
	} else {
		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1024,
		    "Done %s.\n", __func__);
@@ -736,6 +743,7 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t
risc_addr)
quoted
		ql_dbg(ql_dbg_mbx, vha, 0x1026,
		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+		vha->hw_err_cnt++;
		return rval;
	}
@@ -1313,6 +1321,7 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *vha)
	if (rval != QLA_SUCCESS) {
		/*EMPTY*/
		ql_dbg(ql_dbg_mbx, vha, 0x1033, "Failed=%x.\n", rval);
+		vha->hw_err_cnt++;
	} else {
		/*EMPTY*/
		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1034,
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index f80abe28f35a..a760cb38e487 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -1274,6 +1274,8 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
	sp = scsi_cmd_priv(cmd);
	qpair = sp->qpair;

+	vha->cmd_timeout_cnt++;
+
	if ((sp->fcport && sp->fcport->deleted) || !qpair)
		return SUCCESS;
@@ -1442,6 +1444,7 @@ __qla2xxx_eh_generic_reset(char *name, enum
nexus_wait_type type,
quoted
	    "%s RESET FAILED: %s nexus=%ld:%d:%llu cmd=%p.\n", name,
	    reset_errors[err], vha->host_no, cmd->device->id, cmd->device->lun,
	    cmd);
+	vha->reset_cmd_err_cnt++;
	return FAILED;
}
@@ -3141,6 +3144,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const
struct pci_device_id *id)
quoted
	ha->mr.fcport.supported_classes = FC_COS_UNSPECIFIED;
	ha->mr.fcport.scan_state = 1;

+	qla2xxx_reset_stats(host, QLA2XX_HW_ERROR |
QLA2XX_SHT_LNK_DWN |
quoted
+			    QLA2XX_INT_ERR | QLA2XX_CMD_TIMEOUT |
+			    QLA2XX_RESET_CMD_ERR |
QLA2XX_TGT_SHT_LNK_DOWN);
quoted
+
	/* Set the SG table size based on ISP type */
	if (!IS_FWI2_CAPABLE(ha)) {
		if (IS_QLA2100(ha))
@@ -5090,6 +5097,7 @@ void qla24xx_create_new_sess(struct scsi_qla_host
*vha, struct qla_work_evt *e)
quoted
			fcport->d_id = e->u.new_sess.id;
			fcport->flags |= FCF_FABRIC_DEVICE;
			fcport->fw_login_state = DSC_LS_PLOGI_PEND;
+			fcport->tgt_short_link_down_cnt = 0;

			memcpy(fcport->port_name, e-
u.new_sess.port_name,
			    WWN_SIZE);
@@ -7061,6 +7069,8 @@ qla2x00_timer(struct timer_list *t)
	uint16_t        w;
	struct qla_hw_data *ha = vha->hw;
	struct req_que *req;
+	unsigned long flags;
+	fc_port_t *fcport = NULL;

	if (ha->flags.eeh_busy) {
		ql_dbg(ql_dbg_timer, vha, 0x6000,
@@ -7092,6 +7102,16 @@ qla2x00_timer(struct timer_list *t)
	if (!vha->vp_idx && IS_QLAFX00(ha))
		qlafx00_timer_routine(vha);

+	if (vha->link_down_time < QLA2XX_MAX_LINK_DOWN_TIME)
+		vha->link_down_time++;
+
+	spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
+	list_for_each_entry(fcport, &vha->vp_fcports, list) {
+		if (fcport->tgt_link_down_time <
QLA2XX_MAX_LINK_DOWN_TIME)
quoted
+			fcport->tgt_link_down_time++;
+	}
+	spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
+
	/* Loop down handler. */
	if (atomic_read(&vha->loop_down_timer) > 0 &&
	    !(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) &&
--
2.19.0.rc0
--
Himanshu Madhani	 Oracle Linux Engineering
Thanks,
~Saurav
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help