Commit 4c47efc1 authored by James Smart's avatar James Smart Committed by Martin K. Petersen
Browse files

scsi: lpfc: Move SCSI and NVME Stats to hardware queue structures



Many io statistics were being sampled and saved using adapter-based data
structures. This was creating a lot of contention and cache thrashing in
the I/O path.

Move the statistics to the hardware queue data structures.  Given the
per-queue data structures, use of atomic types is lessened.

Add new sysfs and debugfs stat routines to collate the per hardware queue
values and report at an adapter level.

Signed-off-by: default avatarDick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: default avatarJames Smart <jsmart2021@gmail.com>
Reviewed-by: default avatarHannes Reinecke <hare@suse.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 63df6d63
Loading
Loading
Loading
Loading
+1 −8
Original line number Diff line number Diff line
@@ -479,6 +479,7 @@ struct lpfc_vport {
	struct dentry *debug_disc_trc;
	struct dentry *debug_nodelist;
	struct dentry *debug_nvmestat;
	struct dentry *debug_scsistat;
	struct dentry *debug_nvmektime;
	struct dentry *debug_cpucheck;
	struct dentry *vport_debugfs_root;
@@ -946,14 +947,6 @@ struct lpfc_hba {
	struct timer_list eratt_poll;
	uint32_t eratt_poll_interval;

	/*
	 * stat  counters
	 */
	atomic_t fc4ScsiInputRequests;
	atomic_t fc4ScsiOutputRequests;
	atomic_t fc4ScsiControlRequests;
	atomic_t fc4ScsiIoCmpls;

	uint64_t bg_guard_err_cnt;
	uint64_t bg_apptag_err_cnt;
	uint64_t bg_reftag_err_cnt;
+59 −9
Original line number Diff line number Diff line
@@ -64,9 +64,6 @@
#define LPFC_MIN_MRQ_POST	512
#define LPFC_MAX_MRQ_POST	2048

#define LPFC_MAX_NVME_INFO_TMP_LEN	100
#define LPFC_NVME_INFO_MORE_STR		"\nCould be more info...\n"

/*
 * Write key size should be multiple of 4. If write key is changed
 * make sure that library write key is also changed.
@@ -155,7 +152,7 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
	struct lpfc_nvme_rport *rport;
	struct lpfc_nodelist *ndlp;
	struct nvme_fc_remote_port *nrport;
	struct lpfc_nvme_ctrl_stat *cstat;
	struct lpfc_fc4_ctrl_stat *cstat;
	uint64_t data1, data2, data3;
	uint64_t totin, totout, tot;
	char *statep;
@@ -457,12 +454,12 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr,
	totin = 0;
	totout = 0;
	for (i = 0; i < phba->cfg_hdw_queue; i++) {
		cstat = &lport->cstat[i];
		tot = atomic_read(&cstat->fc4NvmeIoCmpls);
		cstat = &phba->sli4_hba.hdwq[i].nvme_cstat;
		tot = cstat->io_cmpls;
		totin += tot;
		data1 = atomic_read(&cstat->fc4NvmeInputRequests);
		data2 = atomic_read(&cstat->fc4NvmeOutputRequests);
		data3 = atomic_read(&cstat->fc4NvmeControlRequests);
		data1 = cstat->input_requests;
		data2 = cstat->output_requests;
		data3 = cstat->control_requests;
		totout += (data1 + data2 + data3);
	}
	scnprintf(tmp, sizeof(tmp),
@@ -508,6 +505,57 @@ buffer_done:
	return len;
}

static ssize_t
lpfc_scsi_stat_show(struct device *dev, struct device_attribute *attr,
		    char *buf)
{
	struct Scsi_Host *shost = class_to_shost(dev);
	struct lpfc_vport *vport = shost_priv(shost);
	struct lpfc_hba *phba = vport->phba;
	int len;
	struct lpfc_fc4_ctrl_stat *cstat;
	u64 data1, data2, data3;
	u64 tot, totin, totout;
	int i;
	char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0};

	if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) ||
	    (phba->sli_rev != LPFC_SLI_REV4))
		return 0;

	scnprintf(buf, PAGE_SIZE, "SCSI HDWQ Statistics\n");

	totin = 0;
	totout = 0;
	for (i = 0; i < phba->cfg_hdw_queue; i++) {
		cstat = &phba->sli4_hba.hdwq[i].scsi_cstat;
		tot = cstat->io_cmpls;
		totin += tot;
		data1 = cstat->input_requests;
		data2 = cstat->output_requests;
		data3 = cstat->control_requests;
		totout += (data1 + data2 + data3);

		scnprintf(tmp, sizeof(tmp), "HDWQ (%d): Rd %016llx Wr %016llx "
			  "IO %016llx ", i, data1, data2, data3);
		if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
			goto buffer_done;

		scnprintf(tmp, sizeof(tmp), "Cmpl %016llx OutIO %016llx\n",
			  tot, ((data1 + data2 + data3) - tot));
		if (strlcat(buf, tmp, PAGE_SIZE) >= PAGE_SIZE)
			goto buffer_done;
	}
	scnprintf(tmp, sizeof(tmp), "Total FCP Cmpl %016llx Issue %016llx "
		  "OutIO %016llx\n", totin, totout, totout - totin);
	strlcat(buf, tmp, PAGE_SIZE);

buffer_done:
	len = strnlen(buf, PAGE_SIZE);

	return len;
}

static ssize_t
lpfc_bg_info_show(struct device *dev, struct device_attribute *attr,
		  char *buf)
@@ -2573,6 +2621,7 @@ lpfc_##attr##_store(struct device *dev, struct device_attribute *attr, \


static DEVICE_ATTR(nvme_info, 0444, lpfc_nvme_info_show, NULL);
static DEVICE_ATTR(scsi_stat, 0444, lpfc_scsi_stat_show, NULL);
static DEVICE_ATTR(bg_info, S_IRUGO, lpfc_bg_info_show, NULL);
static DEVICE_ATTR(bg_guard_err, S_IRUGO, lpfc_bg_guard_err_show, NULL);
static DEVICE_ATTR(bg_apptag_err, S_IRUGO, lpfc_bg_apptag_err_show, NULL);
@@ -5642,6 +5691,7 @@ LPFC_ATTR_RW(enable_dpp, 1, 0, 1, "Enable Direct Packet Push");

struct device_attribute *lpfc_hba_attrs[] = {
	&dev_attr_nvme_info,
	&dev_attr_scsi_stat,
	&dev_attr_bg_info,
	&dev_attr_bg_guard_err,
	&dev_attr_bg_apptag_err,
+150 −8
Original line number Diff line number Diff line
@@ -840,7 +840,7 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
	struct lpfc_nvmet_tgtport *tgtp;
	struct lpfc_nvmet_rcv_ctx *ctxp, *next_ctxp;
	struct nvme_fc_local_port *localport;
	struct lpfc_nvme_ctrl_stat *cstat;
	struct lpfc_fc4_ctrl_stat *cstat;
	struct lpfc_nvme_lport *lport;
	uint64_t data1, data2, data3;
	uint64_t tot, totin, totout;
@@ -979,7 +979,7 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
			return len;

		len += snprintf(buf + len, size - len,
				"\nNVME Lport Statistics\n");
				"\nNVME HDWQ Statistics\n");

		len += snprintf(buf + len, size - len,
				"LS: Xmt %016x Cmpl %016x\n",
@@ -993,12 +993,12 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
		totin = 0;
		totout = 0;
		for (i = 0; i < phba->cfg_hdw_queue; i++) {
			cstat = &lport->cstat[i];
			tot = atomic_read(&cstat->fc4NvmeIoCmpls);
			cstat = &phba->sli4_hba.hdwq[i].nvme_cstat;
			tot = cstat->io_cmpls;
			totin += tot;
			data1 = atomic_read(&cstat->fc4NvmeInputRequests);
			data2 = atomic_read(&cstat->fc4NvmeOutputRequests);
			data3 = atomic_read(&cstat->fc4NvmeControlRequests);
			data1 = cstat->input_requests;
			data2 = cstat->output_requests;
			data3 = cstat->control_requests;
			totout += (data1 + data2 + data3);

			/* Limit to 32, debugfs display buffer limitation */
@@ -1006,7 +1006,7 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
				continue;

			len += snprintf(buf + len, PAGE_SIZE - len,
					"FCP (%d): Rd %016llx Wr %016llx "
					"HDWQ (%d): Rd %016llx Wr %016llx "
					"IO %016llx ",
					i, data1, data2, data3);
			len += snprintf(buf + len, PAGE_SIZE - len,
@@ -1046,6 +1046,66 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size)
	return len;
}

/**
 * lpfc_debugfs_scsistat_data - Dump target node list to a buffer
 * @vport: The vport to gather target node info from.
 * @buf: The buffer to dump log into.
 * @size: The maximum amount of data to process.
 *
 * Description:
 * This routine dumps the SCSI statistics associated with @vport
 *
 * Return Value:
 * This routine returns the amount of bytes that were dumped into @buf and will
 * not exceed @size.
 **/
static int
lpfc_debugfs_scsistat_data(struct lpfc_vport *vport, char *buf, int size)
{
	int len;
	struct lpfc_hba *phba = vport->phba;
	struct lpfc_fc4_ctrl_stat *cstat;
	u64 data1, data2, data3;
	u64 tot, totin, totout;
	int i;
	char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0};

	if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) ||
	    (phba->sli_rev != LPFC_SLI_REV4))
		return 0;

	scnprintf(buf, size, "SCSI HDWQ Statistics\n");

	totin = 0;
	totout = 0;
	for (i = 0; i < phba->cfg_hdw_queue; i++) {
		cstat = &phba->sli4_hba.hdwq[i].scsi_cstat;
		tot = cstat->io_cmpls;
		totin += tot;
		data1 = cstat->input_requests;
		data2 = cstat->output_requests;
		data3 = cstat->control_requests;
		totout += (data1 + data2 + data3);

		scnprintf(tmp, sizeof(tmp), "HDWQ (%d): Rd %016llx Wr %016llx "
			  "IO %016llx ", i, data1, data2, data3);
		if (strlcat(buf, tmp, size) >= size)
			goto buffer_done;

		scnprintf(tmp, sizeof(tmp), "Cmpl %016llx OutIO %016llx\n",
			  tot, ((data1 + data2 + data3) - tot));
		if (strlcat(buf, tmp, size) >= size)
			goto buffer_done;
	}
	scnprintf(tmp, sizeof(tmp), "Total FCP Cmpl %016llx Issue %016llx "
		  "OutIO %016llx\n", totin, totout, totout - totin);
	strlcat(buf, tmp, size);

buffer_done:
	len = strnlen(buf, size);

	return len;
}

/**
 * lpfc_debugfs_nvmektime_data - Dump target node list to a buffer
@@ -2211,6 +2271,64 @@ lpfc_debugfs_nvmestat_write(struct file *file, const char __user *buf,
	return nbytes;
}

static int
lpfc_debugfs_scsistat_open(struct inode *inode, struct file *file)
{
	struct lpfc_vport *vport = inode->i_private;
	struct lpfc_debug *debug;
	int rc = -ENOMEM;

	debug = kmalloc(sizeof(*debug), GFP_KERNEL);
	if (!debug)
		goto out;

	 /* Round to page boundary */
	debug->buffer = kzalloc(LPFC_SCSISTAT_SIZE, GFP_KERNEL);
	if (!debug->buffer) {
		kfree(debug);
		goto out;
	}

	debug->len = lpfc_debugfs_scsistat_data(vport, debug->buffer,
		LPFC_SCSISTAT_SIZE);

	debug->i_private = inode->i_private;
	file->private_data = debug;

	rc = 0;
out:
	return rc;
}

static ssize_t
lpfc_debugfs_scsistat_write(struct file *file, const char __user *buf,
			    size_t nbytes, loff_t *ppos)
{
	struct lpfc_debug *debug = file->private_data;
	struct lpfc_vport *vport = (struct lpfc_vport *)debug->i_private;
	struct lpfc_hba *phba = vport->phba;
	char mybuf[6] = {0};
	int i;

	/* Protect copy from user */
	if (!access_ok(buf, nbytes))
		return -EFAULT;

	if (copy_from_user(mybuf, buf, (nbytes >= sizeof(mybuf)) ?
				       (sizeof(mybuf) - 1) : nbytes))
		return -EFAULT;

	if ((strncmp(&mybuf[0], "reset", strlen("reset")) == 0) ||
	    (strncmp(&mybuf[0], "zero", strlen("zero")) == 0)) {
		for (i = 0; i < phba->cfg_hdw_queue; i++) {
			memset(&phba->sli4_hba.hdwq[i].scsi_cstat, 0,
			       sizeof(phba->sli4_hba.hdwq[i].scsi_cstat));
		}
	}

	return nbytes;
}

static int
lpfc_debugfs_nvmektime_open(struct inode *inode, struct file *file)
{
@@ -4972,6 +5090,16 @@ static const struct file_operations lpfc_debugfs_op_nvmestat = {
	.release =      lpfc_debugfs_release,
};

#undef lpfc_debugfs_op_scsistat
static const struct file_operations lpfc_debugfs_op_scsistat = {
	.owner =        THIS_MODULE,
	.open =         lpfc_debugfs_scsistat_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_debugfs_read,
	.write =	lpfc_debugfs_scsistat_write,
	.release =      lpfc_debugfs_release,
};

#undef lpfc_debugfs_op_nvmektime
static const struct file_operations lpfc_debugfs_op_nvmektime = {
	.owner =        THIS_MODULE,
@@ -5612,6 +5740,17 @@ nvmeio_off:
				    vport->vport_debugfs_root,
				    vport, &lpfc_debugfs_op_nvmestat);

	snprintf(name, sizeof(name), "scsistat");
	vport->debug_scsistat =
		debugfs_create_file(name, 0644,
				    vport->vport_debugfs_root,
				    vport, &lpfc_debugfs_op_scsistat);
	if (!vport->debug_scsistat) {
		lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
				 "0811 Cannot create debugfs scsistat\n");
		goto debug_failed;
	}

	snprintf(name, sizeof(name), "nvmektime");
	vport->debug_nvmektime =
		debugfs_create_file(name, 0644,
@@ -5750,6 +5889,9 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
	debugfs_remove(vport->debug_nvmestat); /* nvmestat */
	vport->debug_nvmestat = NULL;

	debugfs_remove(vport->debug_scsistat); /* scsistat */
	vport->debug_scsistat = NULL;

	debugfs_remove(vport->debug_nvmektime); /* nvmektime */
	vport->debug_nvmektime = NULL;

+3 −0
Original line number Diff line number Diff line
@@ -50,6 +50,9 @@
#define LPFC_CPUCHECK_SIZE 8192
#define LPFC_NVMEIO_TRC_SIZE 8192

/* scsistat output buffer size */
#define LPFC_SCSISTAT_SIZE 8192

#define LPFC_DEBUG_OUT_LINE_SZ	80

/*
+26 −14
Original line number Diff line number Diff line
@@ -1282,7 +1282,7 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
	struct lpfc_register reg_data;
	struct nvme_fc_local_port *localport;
	struct lpfc_nvme_lport *lport;
	struct lpfc_nvme_ctrl_stat *cstat;
	struct lpfc_fc4_ctrl_stat *cstat;
	void __iomem *eqdreg = phba->sli4_hba.u.if_type2.EQDregaddr;

	vports = lpfc_create_vport_work_array(phba);
@@ -1324,16 +1324,13 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba)
				tot = 0;
				for (i = 0;
					i < phba->cfg_hdw_queue; i++) {
					cstat = &lport->cstat[i];
					data1 = atomic_read(
						&cstat->fc4NvmeInputRequests);
					data2 = atomic_read(
						&cstat->fc4NvmeOutputRequests);
					data3 = atomic_read(
						&cstat->fc4NvmeControlRequests);
					cstat =
					     &phba->sli4_hba.hdwq[i].nvme_cstat;
					data1 = cstat->input_requests;
					data2 = cstat->output_requests;
					data3 = cstat->control_requests;
					tot += (data1 + data2 + data3);
					tot -= atomic_read(
						&cstat->fc4NvmeIoCmpls);
					tot -= cstat->io_cmpls;
				}
			}
		}
@@ -7221,10 +7218,6 @@ lpfc_create_shost(struct lpfc_hba *phba)
	phba->fc_arbtov = FF_DEF_ARBTOV;

	atomic_set(&phba->sdev_cnt, 0);
	atomic_set(&phba->fc4ScsiInputRequests, 0);
	atomic_set(&phba->fc4ScsiOutputRequests, 0);
	atomic_set(&phba->fc4ScsiControlRequests, 0);
	atomic_set(&phba->fc4ScsiIoCmpls, 0);
	vport = lpfc_create_port(phba, phba->brd_no, &phba->pcidev->dev);
	if (!vport)
		return -ENODEV;
@@ -8776,6 +8769,25 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
			phba->sli4_hba.nvmet_mrq_data[idx] = qdesc;
		}
	}

#if defined(BUILD_NVME)
	/* Clear NVME stats */
	if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) {
		for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
			memset(&phba->sli4_hba.hdwq[idx].nvme_cstat, 0,
			       sizeof(phba->sli4_hba.hdwq[idx].nvme_cstat));
		}
	}
#endif

	/* Clear SCSI stats */
	if (phba->cfg_enable_fc4_type & LPFC_ENABLE_FCP) {
		for (idx = 0; idx < phba->cfg_hdw_queue; idx++) {
			memset(&phba->sli4_hba.hdwq[idx].scsi_cstat, 0,
			       sizeof(phba->sli4_hba.hdwq[idx].scsi_cstat));
		}
	}

	return 0;

out_error:
Loading