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

scsi: lpfc: Rework MIB Rx Monitor debug info logic

The kernel test robot reported the following sparse warning:

arch/arm64/include/asm/cmpxchg.h:88:1: sparse: sparse: cast truncates
   bits from constant value (369 becomes 69)

On arm64, atomic_xchg only works on 8-bit byte fields.  Thus, the macro
usage of LPFC_RXMONITOR_TABLE_IN_USE can be unintentionally truncated
leading to all logic involving the LPFC_RXMONITOR_TABLE_IN_USE macro to not
work properly.

Replace the Rx Table atomic_t indexing logic with a new
lpfc_rx_info_monitor structure that holds a circular ring buffer.  For
locking semantics, a spinlock_t is used.

Link: https://lore.kernel.org/r/20220819011736.14141-4-jsmart2021@gmail.com


Fixes: 17b27ac5 ("scsi: lpfc: Add rx monitoring statistics")
Cc: <stable@vger.kernel.org> # v5.15+
Co-developed-by: default avatarJustin Tee <justin.tee@broadcom.com>
Signed-off-by: default avatarJustin Tee <justin.tee@broadcom.com>
Signed-off-by: default avatarJames Smart <jsmart2021@gmail.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 59b7e210
Loading
Loading
Loading
Loading
+9 −5
Original line number Diff line number Diff line
@@ -1570,10 +1570,7 @@ struct lpfc_hba {
	u32 cgn_acqe_cnt;

	/* RX monitor handling for CMF */
	struct rxtable_entry *rxtable;  /* RX_monitor information */
	atomic_t rxtable_idx_head;
#define LPFC_RXMONITOR_TABLE_IN_USE     (LPFC_MAX_RXMONITOR_ENTRY + 73)
	atomic_t rxtable_idx_tail;
	struct lpfc_rx_info_monitor *rx_monitor;
	atomic_t rx_max_read_cnt;       /* Maximum read bytes */
	uint64_t rx_block_cnt;

@@ -1622,7 +1619,7 @@ struct lpfc_hba {

#define LPFC_MAX_RXMONITOR_ENTRY	800
#define LPFC_MAX_RXMONITOR_DUMP		32
struct rxtable_entry {
struct rx_info_entry {
	uint64_t cmf_bytes;	/* Total no of read bytes for CMF_SYNC_WQE */
	uint64_t total_bytes;   /* Total no of read bytes requested */
	uint64_t rcv_bytes;     /* Total no of read bytes completed */
@@ -1637,6 +1634,13 @@ struct rxtable_entry {
	uint32_t timer_interval;
};

struct lpfc_rx_info_monitor {
	struct rx_info_entry *ring; /* info organized in a circular buffer */
	u32 head_idx, tail_idx; /* index to head/tail of ring */
	spinlock_t lock; /* spinlock for ring */
	u32 entries; /* storing number entries/size of ring */
};

static inline struct Scsi_Host *
lpfc_shost_from_vport(struct lpfc_vport *vport)
{
+8 −0
Original line number Diff line number Diff line
@@ -92,6 +92,14 @@ void lpfc_cgn_dump_rxmonitor(struct lpfc_hba *phba);
void lpfc_cgn_update_stat(struct lpfc_hba *phba, uint32_t dtag);
void lpfc_unblock_requests(struct lpfc_hba *phba);
void lpfc_block_requests(struct lpfc_hba *phba);
int lpfc_rx_monitor_create_ring(struct lpfc_rx_info_monitor *rx_monitor,
				u32 entries);
void lpfc_rx_monitor_destroy_ring(struct lpfc_rx_info_monitor *rx_monitor);
void lpfc_rx_monitor_record(struct lpfc_rx_info_monitor *rx_monitor,
			    struct rx_info_entry *entry);
u32 lpfc_rx_monitor_report(struct lpfc_hba *phba,
			   struct lpfc_rx_info_monitor *rx_monitor, char *buf,
			   u32 buf_len, u32 max_read_entries);

void lpfc_mbx_cmpl_local_config_link(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_mbx_cmpl_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *);
+10 −49
Original line number Diff line number Diff line
@@ -5531,7 +5531,7 @@ lpfc_rx_monitor_open(struct inode *inode, struct file *file)
	if (!debug)
		goto out;

	debug->buffer = vmalloc(MAX_DEBUGFS_RX_TABLE_SIZE);
	debug->buffer = vmalloc(MAX_DEBUGFS_RX_INFO_SIZE);
	if (!debug->buffer) {
		kfree(debug);
		goto out;
@@ -5552,57 +5552,18 @@ lpfc_rx_monitor_read(struct file *file, char __user *buf, size_t nbytes,
	struct lpfc_rx_monitor_debug *debug = file->private_data;
	struct lpfc_hba *phba = (struct lpfc_hba *)debug->i_private;
	char *buffer = debug->buffer;
	struct rxtable_entry *entry;
	int i, len = 0, head, tail, last, start;

	head = atomic_read(&phba->rxtable_idx_head);
	while (head == LPFC_RXMONITOR_TABLE_IN_USE) {
		/* Table is getting updated */
		msleep(20);
		head = atomic_read(&phba->rxtable_idx_head);
	if (!phba->rx_monitor) {
		scnprintf(buffer, MAX_DEBUGFS_RX_INFO_SIZE,
			  "Rx Monitor Info is empty.\n");
	} else {
		lpfc_rx_monitor_report(phba, phba->rx_monitor, buffer,
				       MAX_DEBUGFS_RX_INFO_SIZE,
				       LPFC_MAX_RXMONITOR_ENTRY);
	}

	tail = atomic_xchg(&phba->rxtable_idx_tail, head);
	if (!phba->rxtable || head == tail) {
		len += scnprintf(buffer + len, MAX_DEBUGFS_RX_TABLE_SIZE - len,
				"Rxtable is empty\n");
		goto out;
	}
	last = (head > tail) ?  head : LPFC_MAX_RXMONITOR_ENTRY;
	start = tail;

	len += scnprintf(buffer + len, MAX_DEBUGFS_RX_TABLE_SIZE - len,
			"        MaxBPI    Tot_Data_CMF Tot_Data_Cmd "
			"Tot_Data_Cmpl  Lat(us)  Avg_IO  Max_IO "
			"Bsy IO_cnt Info BWutil(ms)\n");
get_table:
	for (i = start; i < last; i++) {
		entry = &phba->rxtable[i];
		len += scnprintf(buffer + len, MAX_DEBUGFS_RX_TABLE_SIZE - len,
				"%3d:%12lld %12lld %12lld %12lld "
				"%7lldus %8lld %7lld "
				"%2d   %4d   %2d   %2d(%2d)\n",
				i, entry->max_bytes_per_interval,
				entry->cmf_bytes,
				entry->total_bytes,
				entry->rcv_bytes,
				entry->avg_io_latency,
				entry->avg_io_size,
				entry->max_read_cnt,
				entry->cmf_busy,
				entry->io_cnt,
				entry->cmf_info,
				entry->timer_utilization,
				entry->timer_interval);
	}

	if (head != last) {
		start = 0;
		last = head;
		goto get_table;
	}
out:
	return simple_read_from_buffer(buf, nbytes, ppos, buffer, len);
	return simple_read_from_buffer(buf, nbytes, ppos, buffer,
				       strlen(buffer));
}

static int
+1 −1
Original line number Diff line number Diff line
@@ -282,7 +282,7 @@ struct lpfc_idiag {
	void *ptr_private;
};

#define MAX_DEBUGFS_RX_TABLE_SIZE	(128 * LPFC_MAX_RXMONITOR_ENTRY)
#define MAX_DEBUGFS_RX_INFO_SIZE	(128 * LPFC_MAX_RXMONITOR_ENTRY)
struct lpfc_rx_monitor_debug {
	char *i_private;
	char *buffer;
+23 −60
Original line number Diff line number Diff line
@@ -5569,38 +5569,12 @@ lpfc_async_link_speed_to_read_top(struct lpfc_hba *phba, uint8_t speed_code)
void
lpfc_cgn_dump_rxmonitor(struct lpfc_hba *phba)
{
	struct rxtable_entry *entry;
	int cnt = 0, head, tail, last, start;

	head = atomic_read(&phba->rxtable_idx_head);
	tail = atomic_read(&phba->rxtable_idx_tail);
	if (!phba->rxtable || head == tail) {
		lpfc_printf_log(phba, KERN_ERR, LOG_CGN_MGMT,
				"4411 Rxtable is empty\n");
		return;
	}
	last = tail;
	start = head;

	/* Display the last LPFC_MAX_RXMONITOR_DUMP entries from the rxtable */
	while (start != last) {
		if (start)
			start--;
		else
			start = LPFC_MAX_RXMONITOR_ENTRY - 1;
		entry = &phba->rxtable[start];
	if (!phba->rx_monitor) {
		lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT,
				"4410 %02d: MBPI %lld Xmit %lld Cmpl %lld "
				"Lat %lld ASz %lld Info %02d BWUtil %d "
				"Int %d slot %d\n",
				cnt, entry->max_bytes_per_interval,
				entry->total_bytes, entry->rcv_bytes,
				entry->avg_io_latency, entry->avg_io_size,
				entry->cmf_info, entry->timer_utilization,
				entry->timer_interval, start);
		cnt++;
		if (cnt >= LPFC_MAX_RXMONITOR_DUMP)
			return;
				"4411 Rx Monitor Info is empty.\n");
	} else {
		lpfc_rx_monitor_report(phba, phba->rx_monitor, NULL, 0,
				       LPFC_MAX_RXMONITOR_DUMP);
	}
}

@@ -6007,9 +5981,8 @@ lpfc_cmf_timer(struct hrtimer *timer)
{
	struct lpfc_hba *phba = container_of(timer, struct lpfc_hba,
					     cmf_timer);
	struct rxtable_entry *entry;
	struct rx_info_entry entry;
	uint32_t io_cnt;
	uint32_t head, tail;
	uint32_t busy, max_read;
	uint64_t total, rcv, lat, mbpi, extra, cnt;
	int timer_interval = LPFC_CMF_INTERVAL;
@@ -6129,40 +6102,30 @@ lpfc_cmf_timer(struct hrtimer *timer)
	}

	/* Save rxmonitor information for debug */
	if (phba->rxtable) {
		head = atomic_xchg(&phba->rxtable_idx_head,
				   LPFC_RXMONITOR_TABLE_IN_USE);
		entry = &phba->rxtable[head];
		entry->total_bytes = total;
		entry->cmf_bytes = total + extra;
		entry->rcv_bytes = rcv;
		entry->cmf_busy = busy;
		entry->cmf_info = phba->cmf_active_info;
	if (phba->rx_monitor) {
		entry.total_bytes = total;
		entry.cmf_bytes = total + extra;
		entry.rcv_bytes = rcv;
		entry.cmf_busy = busy;
		entry.cmf_info = phba->cmf_active_info;
		if (io_cnt) {
			entry->avg_io_latency = div_u64(lat, io_cnt);
			entry->avg_io_size = div_u64(rcv, io_cnt);
			entry.avg_io_latency = div_u64(lat, io_cnt);
			entry.avg_io_size = div_u64(rcv, io_cnt);
		} else {
			entry->avg_io_latency = 0;
			entry->avg_io_size = 0;
			entry.avg_io_latency = 0;
			entry.avg_io_size = 0;
		}
		entry->max_read_cnt = max_read;
		entry->io_cnt = io_cnt;
		entry->max_bytes_per_interval = mbpi;
		entry.max_read_cnt = max_read;
		entry.io_cnt = io_cnt;
		entry.max_bytes_per_interval = mbpi;
		if (phba->cmf_active_mode == LPFC_CFG_MANAGED)
			entry->timer_utilization = phba->cmf_last_ts;
			entry.timer_utilization = phba->cmf_last_ts;
		else
			entry->timer_utilization = ms;
		entry->timer_interval = ms;
			entry.timer_utilization = ms;
		entry.timer_interval = ms;
		phba->cmf_last_ts = 0;

		/* Increment rxtable index */
		head = (head + 1) % LPFC_MAX_RXMONITOR_ENTRY;
		tail = atomic_read(&phba->rxtable_idx_tail);
		if (head == tail) {
			tail = (tail + 1) % LPFC_MAX_RXMONITOR_ENTRY;
			atomic_set(&phba->rxtable_idx_tail, tail);
		}
		atomic_set(&phba->rxtable_idx_head, head);
		lpfc_rx_monitor_record(phba->rx_monitor, &entry);
	}

	if (phba->cmf_active_mode == LPFC_CFG_MONITOR) {
Loading