Commit dbf1f53c authored by Saurav Kashyap's avatar Saurav Kashyap Committed by Martin K. Petersen
Browse files

scsi: qla2xxx: Implementation to get and manage host, target stats and initiator port

This statistics will help in debugging process and checking specific error
counts. It also provides a capability to isolate the port or bring it out
of isolation.

Link: https://lore.kernel.org/r/20210111093134.1206-2-njavali@marvell.com


Reported-by: default avatarkernel test robot <lkp@intel.com>
Reviewed-by: default avatarHimanshu Madhani <himanshu.madhani@oracle.com>
Signed-off-by: default avatarSaurav Kashyap <skashyap@marvell.com>
Signed-off-by: default avatarNilesh Javali <njavali@marvell.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent ac341c2d
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -710,6 +710,12 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj,
		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;
}
+342 −0
Original line number Diff line number Diff line
@@ -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)
	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");
		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 QLA_STOP:
		ret = qla2xxx_stop_stats(vha->host, req_data->stat_type);
		break;
	case QLA_START:
		ret = qla2xxx_start_stats(vha->host, req_data->stat_type);
		break;
	case QLA_CLEAR:
		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;
	}

	kfree(req_data);

	/* Prepare response */
	rsp_data.status = ret;
	bsg_job->reply_payload.payload_len = sizeof(struct ql_vnd_mng_host_stats_resp);

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

	/* 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;
		bsg_job->reply_payload.payload_len = sizeof(struct ql_vnd_mng_host_stats_resp);

		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);
		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);
	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");
		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;
		bsg_job->reply_payload.payload_len = sizeof(struct ql_vnd_mng_host_stats_resp);

		bsg_reply->reply_payload_rcv_len =
			sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
					    bsg_job->reply_payload.sg_cnt, &data,
					    sizeof(struct ql_vnd_tgt_stats_resp));

		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);
		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");
		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 QLA_ENABLE:
		ret = qla2xxx_enable_port(vha->host);
		break;
	case QLA_DISABLE:
		ret = qla2xxx_disable_port(vha->host);
		break;
	default:
		ql_log(ql_log_warn, vha, 0x0000, "Invalid action.\n");
		ret = -EIO;
		break;
	}

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

	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)
	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 ||
	    bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS)
		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);

+5 −0
Original line number Diff line number Diff line
@@ -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

+71 −0
Original line number Diff line number Diff line
@@ -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;
	} 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 {
	QLA_STOP = 0,
	QLA_START,
	QLA_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 {
	QLA_ENABLE = 0,
	QLA_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"
+23 −0
Original line number Diff line number Diff line
@@ -945,4 +945,27 @@ extern void qla2x00_dfs_remove_rport(scsi_qla_host_t *vha, struct fc_port *fp);
/* 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);
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 */
Loading