Commit 840eda96 authored by James Smart's avatar James Smart Committed by Martin K. Petersen
Browse files

scsi: lpfc: Fix erroneous cpu limit of 128 on I/O statistics

The cpu io statistics were capped by a hard define limit of 128. This
effectively was a max number of CPUs, not an actual CPU count, nor actual
CPU numbers which can be even larger than both of those values. This made
stats off/misleading and on large CPU count systems, wrong.

Fix the stats so that all CPUs can have a stats struct.  Fix the looping
such that it loops by hdwq, finds CPUs that used the hdwq, and sum the
stats, then display.

Link: https://lore.kernel.org/r/20200322181304.37655-9-jsmart2021@gmail.com


Signed-off-by: default avatarJames Smart <jsmart2021@gmail.com>
Signed-off-by: default avatarDick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 807e7353
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -481,7 +481,7 @@ struct lpfc_vport {
	struct dentry *debug_nvmestat;
	struct dentry *debug_scsistat;
	struct dentry *debug_nvmektime;
	struct dentry *debug_cpucheck;
	struct dentry *debug_hdwqstat;
	struct dentry *vport_debugfs_root;
	struct lpfc_debugfs_trc *disc_trc;
	atomic_t disc_trc_cnt;
@@ -1175,12 +1175,11 @@ struct lpfc_hba {
	uint16_t sfp_warning;

#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
	uint16_t cpucheck_on;
	uint16_t hdwqstat_on;
#define LPFC_CHECK_OFF		0
#define LPFC_CHECK_NVME_IO	1
#define LPFC_CHECK_NVMET_RCV	2
#define LPFC_CHECK_NVMET_IO	4
#define LPFC_CHECK_SCSI_IO	8
#define LPFC_CHECK_NVMET_IO	2
#define LPFC_CHECK_SCSI_IO	4
	uint16_t ktime_on;
	uint64_t ktime_data_samples;
	uint64_t ktime_status_samples;
+120 −84
Original line number Diff line number Diff line
@@ -1603,42 +1603,50 @@ out:
}

/**
 * lpfc_debugfs_cpucheck_data - Dump target node list to a buffer
 * lpfc_debugfs_hdwqstat_data - Dump I/O stats 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 NVME statistics associated with @vport
 * This routine dumps the NVME + 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_cpucheck_data(struct lpfc_vport *vport, char *buf, int size)
lpfc_debugfs_hdwqstat_data(struct lpfc_vport *vport, char *buf, int size)
{
	struct lpfc_hba   *phba = vport->phba;
	struct lpfc_sli4_hdw_queue *qp;
	int i, j, max_cnt;
	int len = 0;
	struct lpfc_hdwq_stat *c_stat;
	int i, j, len;
	uint32_t tot_xmt;
	uint32_t tot_rcv;
	uint32_t tot_cmpl;
	char tmp[LPFC_MAX_SCSI_INFO_TMP_LEN] = {0};

	len += scnprintf(buf + len, PAGE_SIZE - len,
			"CPUcheck %s ",
			(phba->cpucheck_on & LPFC_CHECK_NVME_IO ?
	scnprintf(tmp, sizeof(tmp), "HDWQ Stats:\n\n");
	if (strlcat(buf, tmp, size) >= size)
		goto buffer_done;

	scnprintf(tmp, sizeof(tmp), "(NVME Accounting: %s) ",
		  (phba->hdwqstat_on &
		  (LPFC_CHECK_NVME_IO | LPFC_CHECK_NVMET_IO) ?
		  "Enabled" : "Disabled"));
	if (phba->nvmet_support) {
		len += scnprintf(buf + len, PAGE_SIZE - len,
				"%s\n",
				(phba->cpucheck_on & LPFC_CHECK_NVMET_RCV ?
					"Rcv Enabled\n" : "Rcv Disabled\n"));
	} else {
		len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
	}
	max_cnt = size - LPFC_DEBUG_OUT_LINE_SZ;
	if (strlcat(buf, tmp, size) >= size)
		goto buffer_done;

	scnprintf(tmp, sizeof(tmp), "(SCSI Accounting: %s) ",
		  (phba->hdwqstat_on & LPFC_CHECK_SCSI_IO ?
		  "Enabled" : "Disabled"));
	if (strlcat(buf, tmp, size) >= size)
		goto buffer_done;

	scnprintf(tmp, sizeof(tmp), "\n\n");
	if (strlcat(buf, tmp, size) >= size)
		goto buffer_done;

	for (i = 0; i < phba->cfg_hdw_queue; i++) {
		qp = &phba->sli4_hba.hdwq[i];
@@ -1646,46 +1654,76 @@ lpfc_debugfs_cpucheck_data(struct lpfc_vport *vport, char *buf, int size)
		tot_rcv = 0;
		tot_xmt = 0;
		tot_cmpl = 0;
		for (j = 0; j < LPFC_CHECK_CPU_CNT; j++) {
			tot_xmt += qp->cpucheck_xmt_io[j];
			tot_cmpl += qp->cpucheck_cmpl_io[j];
			if (phba->nvmet_support)
				tot_rcv += qp->cpucheck_rcv_io[j];
		}

		/* Only display Hardware Qs with something */
		if (!tot_xmt && !tot_cmpl && !tot_rcv)
		for_each_present_cpu(j) {
			c_stat = per_cpu_ptr(phba->sli4_hba.c_stat, j);

			/* Only display for this HDWQ */
			if (i != c_stat->hdwq_no)
				continue;

		len += scnprintf(buf + len, PAGE_SIZE - len,
				"HDWQ %03d: ", i);
		for (j = 0; j < LPFC_CHECK_CPU_CNT; j++) {
			/* Only display non-zero counters */
			if (!qp->cpucheck_xmt_io[j] &&
			    !qp->cpucheck_cmpl_io[j] &&
			    !qp->cpucheck_rcv_io[j])
			if (!c_stat->xmt_io && !c_stat->cmpl_io &&
			    !c_stat->rcv_io)
				continue;

			if (!tot_xmt && !tot_cmpl && !tot_rcv) {
				/* Print HDWQ string only the first time */
				scnprintf(tmp, sizeof(tmp), "[HDWQ %d]:\t", i);
				if (strlcat(buf, tmp, size) >= size)
					goto buffer_done;
			}

			tot_xmt += c_stat->xmt_io;
			tot_cmpl += c_stat->cmpl_io;
			if (phba->nvmet_support)
				tot_rcv += c_stat->rcv_io;

			scnprintf(tmp, sizeof(tmp), "| [CPU %d]: ", j);
			if (strlcat(buf, tmp, size) >= size)
				goto buffer_done;

			if (phba->nvmet_support) {
				len += scnprintf(buf + len, PAGE_SIZE - len,
						"CPU %03d: %x/%x/%x ", j,
						qp->cpucheck_rcv_io[j],
						qp->cpucheck_xmt_io[j],
						qp->cpucheck_cmpl_io[j]);
				scnprintf(tmp, sizeof(tmp),
					  "XMT 0x%x CMPL 0x%x RCV 0x%x |",
					  c_stat->xmt_io, c_stat->cmpl_io,
					  c_stat->rcv_io);
				if (strlcat(buf, tmp, size) >= size)
					goto buffer_done;
			} else {
				len += scnprintf(buf + len, PAGE_SIZE - len,
						"CPU %03d: %x/%x ", j,
						qp->cpucheck_xmt_io[j],
						qp->cpucheck_cmpl_io[j]);
				scnprintf(tmp, sizeof(tmp),
					  "XMT 0x%x CMPL 0x%x |",
					  c_stat->xmt_io, c_stat->cmpl_io);
				if (strlcat(buf, tmp, size) >= size)
					goto buffer_done;
			}
		}
		len += scnprintf(buf + len, PAGE_SIZE - len,
				"Total: %x\n", tot_xmt);
		if (len >= max_cnt) {
			len += scnprintf(buf + len, PAGE_SIZE - len,
					"Truncated ...\n");
			return len;

		/* Check if nothing to display */
		if (!tot_xmt && !tot_cmpl && !tot_rcv)
			continue;

		scnprintf(tmp, sizeof(tmp), "\t->\t[HDWQ Total: ");
		if (strlcat(buf, tmp, size) >= size)
			goto buffer_done;

		if (phba->nvmet_support) {
			scnprintf(tmp, sizeof(tmp),
				  "XMT 0x%x CMPL 0x%x RCV 0x%x]\n\n",
				  tot_xmt, tot_cmpl, tot_rcv);
			if (strlcat(buf, tmp, size) >= size)
				goto buffer_done;
		} else {
			scnprintf(tmp, sizeof(tmp),
				  "XMT 0x%x CMPL 0x%x]\n\n",
				  tot_xmt, tot_cmpl);
			if (strlcat(buf, tmp, size) >= size)
				goto buffer_done;
		}
	}

buffer_done:
	len = strnlen(buf, size);
	return len;
}

@@ -2921,7 +2959,7 @@ lpfc_debugfs_nvmeio_trc_write(struct file *file, const char __user *buf,
}

static int
lpfc_debugfs_cpucheck_open(struct inode *inode, struct file *file)
lpfc_debugfs_hdwqstat_open(struct inode *inode, struct file *file)
{
	struct lpfc_vport *vport = inode->i_private;
	struct lpfc_debug *debug;
@@ -2932,14 +2970,14 @@ lpfc_debugfs_cpucheck_open(struct inode *inode, struct file *file)
		goto out;

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

	debug->len = lpfc_debugfs_cpucheck_data(vport, debug->buffer,
		LPFC_CPUCHECK_SIZE);
	debug->len = lpfc_debugfs_hdwqstat_data(vport, debug->buffer,
						LPFC_SCSISTAT_SIZE);

	debug->i_private = inode->i_private;
	file->private_data = debug;
@@ -2950,16 +2988,16 @@ out:
}

static ssize_t
lpfc_debugfs_cpucheck_write(struct file *file, const char __user *buf,
lpfc_debugfs_hdwqstat_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;
	struct lpfc_sli4_hdw_queue *qp;
	struct lpfc_hdwq_stat *c_stat;
	char mybuf[64];
	char *pbuf;
	int i, j;
	int i;

	if (nbytes > 64)
		nbytes = 64;
@@ -2972,41 +3010,39 @@ lpfc_debugfs_cpucheck_write(struct file *file, const char __user *buf,

	if ((strncmp(pbuf, "on", sizeof("on") - 1) == 0)) {
		if (phba->nvmet_support)
			phba->cpucheck_on |= LPFC_CHECK_NVMET_IO;
			phba->hdwqstat_on |= LPFC_CHECK_NVMET_IO;
		else
			phba->cpucheck_on |= (LPFC_CHECK_NVME_IO |
			phba->hdwqstat_on |= (LPFC_CHECK_NVME_IO |
				LPFC_CHECK_SCSI_IO);
		return strlen(pbuf);
	} else if ((strncmp(pbuf, "nvme_on", sizeof("nvme_on") - 1) == 0)) {
		if (phba->nvmet_support)
			phba->cpucheck_on |= LPFC_CHECK_NVMET_IO;
			phba->hdwqstat_on |= LPFC_CHECK_NVMET_IO;
		else
			phba->cpucheck_on |= LPFC_CHECK_NVME_IO;
			phba->hdwqstat_on |= LPFC_CHECK_NVME_IO;
		return strlen(pbuf);
	} else if ((strncmp(pbuf, "scsi_on", sizeof("scsi_on") - 1) == 0)) {
		phba->cpucheck_on |= LPFC_CHECK_SCSI_IO;
		if (!phba->nvmet_support)
			phba->hdwqstat_on |= LPFC_CHECK_SCSI_IO;
		return strlen(pbuf);
	} else if ((strncmp(pbuf, "rcv",
		   sizeof("rcv") - 1) == 0)) {
		if (phba->nvmet_support)
			phba->cpucheck_on |= LPFC_CHECK_NVMET_RCV;
		else
			return -EINVAL;
	} else if ((strncmp(pbuf, "nvme_off", sizeof("nvme_off") - 1) == 0)) {
		phba->hdwqstat_on &= ~(LPFC_CHECK_NVME_IO |
				       LPFC_CHECK_NVMET_IO);
		return strlen(pbuf);
	} else if ((strncmp(pbuf, "scsi_off", sizeof("scsi_off") - 1) == 0)) {
		phba->hdwqstat_on &= ~LPFC_CHECK_SCSI_IO;
		return strlen(pbuf);
	} else if ((strncmp(pbuf, "off",
		   sizeof("off") - 1) == 0)) {
		phba->cpucheck_on = LPFC_CHECK_OFF;
		phba->hdwqstat_on = LPFC_CHECK_OFF;
		return strlen(pbuf);
	} else if ((strncmp(pbuf, "zero",
		   sizeof("zero") - 1) == 0)) {
		for (i = 0; i < phba->cfg_hdw_queue; i++) {
			qp = &phba->sli4_hba.hdwq[i];

			for (j = 0; j < LPFC_CHECK_CPU_CNT; j++) {
				qp->cpucheck_rcv_io[j] = 0;
				qp->cpucheck_xmt_io[j] = 0;
				qp->cpucheck_cmpl_io[j] = 0;
			}
		for_each_present_cpu(i) {
			c_stat = per_cpu_ptr(phba->sli4_hba.c_stat, i);
			c_stat->xmt_io = 0;
			c_stat->cmpl_io = 0;
			c_stat->rcv_io = 0;
		}
		return strlen(pbuf);
	}
@@ -5451,13 +5487,13 @@ static const struct file_operations lpfc_debugfs_op_nvmeio_trc = {
	.release =      lpfc_debugfs_release,
};

#undef lpfc_debugfs_op_cpucheck
static const struct file_operations lpfc_debugfs_op_cpucheck = {
#undef lpfc_debugfs_op_hdwqstat
static const struct file_operations lpfc_debugfs_op_hdwqstat = {
	.owner =        THIS_MODULE,
	.open =         lpfc_debugfs_cpucheck_open,
	.open =         lpfc_debugfs_hdwqstat_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_debugfs_read,
	.write =	lpfc_debugfs_cpucheck_write,
	.write =	lpfc_debugfs_hdwqstat_write,
	.release =      lpfc_debugfs_release,
};

@@ -6081,11 +6117,11 @@ nvmeio_off:
				    vport->vport_debugfs_root,
				    vport, &lpfc_debugfs_op_nvmektime);

	snprintf(name, sizeof(name), "cpucheck");
	vport->debug_cpucheck =
	snprintf(name, sizeof(name), "hdwqstat");
	vport->debug_hdwqstat =
		debugfs_create_file(name, 0644,
				    vport->vport_debugfs_root,
				    vport, &lpfc_debugfs_op_cpucheck);
				    vport, &lpfc_debugfs_op_hdwqstat);

	/*
	 * The following section is for additional directories/files for the
@@ -6219,8 +6255,8 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
	debugfs_remove(vport->debug_nvmektime); /* nvmektime */
	vport->debug_nvmektime = NULL;

	debugfs_remove(vport->debug_cpucheck); /* cpucheck */
	vport->debug_cpucheck = NULL;
	debugfs_remove(vport->debug_hdwqstat); /* hdwqstat */
	vport->debug_hdwqstat = NULL;

	if (vport->vport_debugfs_root) {
		debugfs_remove(vport->vport_debugfs_root); /* vportX */
+0 −1
Original line number Diff line number Diff line
@@ -47,7 +47,6 @@
/* nvmestat output buffer size */
#define LPFC_NVMESTAT_SIZE 8192
#define LPFC_NVMEKTIME_SIZE 8192
#define LPFC_CPUCHECK_SIZE 8192
#define LPFC_NVMEIO_TRC_SIZE 8192

/* scsistat output buffer size */
+28 −0
Original line number Diff line number Diff line
@@ -6951,6 +6951,17 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
		rc = -ENOMEM;
		goto out_free_hba_cpu_map;
	}

#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
	phba->sli4_hba.c_stat = alloc_percpu(struct lpfc_hdwq_stat);
	if (!phba->sli4_hba.c_stat) {
		lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
				"3332 Failed allocating per cpu hdwq stats\n");
		rc = -ENOMEM;
		goto out_free_hba_eq_info;
	}
#endif

	/*
	 * Enable sr-iov virtual functions if supported and configured
	 * through the module parameter.
@@ -6970,6 +6981,10 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)

	return 0;

#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
out_free_hba_eq_info:
	free_percpu(phba->sli4_hba.eq_info);
#endif
out_free_hba_cpu_map:
	kfree(phba->sli4_hba.cpu_map);
out_free_hba_eq_hdl:
@@ -7008,6 +7023,9 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba)
	struct lpfc_fcf_conn_entry *conn_entry, *next_conn_entry;

	free_percpu(phba->sli4_hba.eq_info);
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
	free_percpu(phba->sli4_hba.c_stat);
#endif

	/* Free memory allocated for msi-x interrupt vector to CPU mapping */
	kfree(phba->sli4_hba.cpu_map);
@@ -10848,6 +10866,9 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors)
#ifdef CONFIG_X86
	struct cpuinfo_x86 *cpuinfo;
#endif
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
	struct lpfc_hdwq_stat *c_stat;
#endif

	max_phys_id = 0;
	min_phys_id = LPFC_VECTOR_MAP_EMPTY;
@@ -11099,10 +11120,17 @@ found_any:
	idx = 0;
	for_each_possible_cpu(cpu) {
		cpup = &phba->sli4_hba.cpu_map[cpu];
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
		c_stat = per_cpu_ptr(phba->sli4_hba.c_stat, cpu);
		c_stat->hdwq_no = cpup->hdwq;
#endif
		if (cpup->hdwq != LPFC_VECTOR_MAP_EMPTY)
			continue;

		cpup->hdwq = idx++ % phba->cfg_hdw_queue;
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
		c_stat->hdwq_no = cpup->hdwq;
#endif
		lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
				"3340 Set Affinity: not present "
				"CPU %d hdwq %d\n",
+21 −24
Original line number Diff line number Diff line
@@ -1012,6 +1012,9 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn,
	uint32_t code, status, idx;
	uint16_t cid, sqhd, data;
	uint32_t *ptr;
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
	int cpu;
#endif

	/* Sanity check on return of outstanding command */
	if (!lpfc_ncmd) {
@@ -1184,19 +1187,15 @@ out_err:
		phba->ktime_last_cmd = lpfc_ncmd->ts_data_nvme;
		lpfc_nvme_ktime(phba, lpfc_ncmd);
	}
	if (unlikely(phba->cpucheck_on & LPFC_CHECK_NVME_IO)) {
		uint32_t cpu;
		idx = lpfc_ncmd->cur_iocbq.hba_wqidx;
	if (unlikely(phba->hdwqstat_on & LPFC_CHECK_NVME_IO)) {
		cpu = raw_smp_processor_id();
		if (cpu < LPFC_CHECK_CPU_CNT) {
		this_cpu_inc(phba->sli4_hba.c_stat->cmpl_io);
		if (lpfc_ncmd->cpu != cpu)
			lpfc_printf_vlog(vport,
					 KERN_INFO, LOG_NVME_IOERR,
					 "6701 CPU Check cmpl: "
					 "cpu %d expect %d\n",
					 cpu, lpfc_ncmd->cpu);
			phba->sli4_hba.hdwq[idx].cpucheck_cmpl_io[cpu]++;
		}
	}
#endif

@@ -1745,9 +1744,9 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport,
	if (lpfc_ncmd->ts_cmd_start)
		lpfc_ncmd->ts_cmd_wqput = ktime_get_ns();

	if (phba->cpucheck_on & LPFC_CHECK_NVME_IO) {
	if (phba->hdwqstat_on & LPFC_CHECK_NVME_IO) {
		cpu = raw_smp_processor_id();
		if (cpu < LPFC_CHECK_CPU_CNT) {
		this_cpu_inc(phba->sli4_hba.c_stat->xmt_io);
		lpfc_ncmd->cpu = cpu;
		if (idx != cpu)
			lpfc_printf_vlog(vport,
@@ -1756,8 +1755,6 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport,
					"cpu %d wq %d\n",
					lpfc_ncmd->cpu,
					lpfc_queue_info->index);
			phba->sli4_hba.hdwq[idx].cpucheck_xmt_io[cpu]++;
		}
	}
#endif
	return 0;
Loading