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

scsi: lpfc: Make FW logging dynamically configurable

Currently, the FW logging facility is a load/boot time parameter which
requires the driver to be unloaded/reloaded or the system rebooted in order
to change its configuration.

Convert the logging facility to allow dynamic enablement and configuration.
Specifically:

 - Convert the feature so that it can be enabled dynamically via an
   attribute.  Additionally, the size of the buffer can be configured
   dynamically.

 - Add locks around states that now may be changing.

 - Tie the feature into debugfs so that the logs can be read at any time.

Link: https://lore.kernel.org/r/20191018211832.7917-12-jsmart2021@gmail.com


Signed-off-by: default avatarDick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: default avatarJames Smart <jsmart2021@gmail.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 8156d378
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -605,6 +605,12 @@ struct lpfc_epd_pool {
	spinlock_t lock;	/* lock for expedite pool */
};

enum ras_state {
	INACTIVE,
	REG_INPROGRESS,
	ACTIVE
};

struct lpfc_ras_fwlog {
	uint8_t *fwlog_buff;
	uint32_t fw_buffcount; /* Buffer size posted to FW */
@@ -621,7 +627,7 @@ struct lpfc_ras_fwlog {
	bool ras_enabled;   /* Ras Enabled for the function */
#define LPFC_RAS_DISABLE_LOGGING 0x00
#define LPFC_RAS_ENABLE_LOGGING 0x01
	bool ras_active;    /* RAS logging running state */
	enum ras_state state;    /* RAS logging running state */
};

struct lpfc_hba {
@@ -1053,6 +1059,7 @@ struct lpfc_hba {
#ifdef LPFC_HDWQ_LOCK_STAT
	struct dentry *debug_lockstat;
#endif
	struct dentry *debug_ras_log;
	atomic_t nvmeio_trc_cnt;
	uint32_t nvmeio_trc_size;
	uint32_t nvmeio_trc_output_idx;
+47 −1
Original line number Diff line number Diff line
@@ -5932,7 +5932,53 @@ LPFC_ATTR_RW(enable_mds_diags, 0, 0, 1, "Enable MDS Diagnostics");
 *	[1-4] = Multiple of 1/4th Mb of host memory for FW logging
 * Value range [0..4]. Default value is 0
 */
LPFC_ATTR_RW(ras_fwlog_buffsize, 0, 0, 4, "Host memory for FW logging");
LPFC_ATTR(ras_fwlog_buffsize, 0, 0, 4, "Host memory for FW logging");
lpfc_param_show(ras_fwlog_buffsize);

static ssize_t
lpfc_ras_fwlog_buffsize_set(struct lpfc_hba  *phba, uint val)
{
	int ret = 0;
	enum ras_state state;

	if (!lpfc_rangecheck(val, 0, 4))
		return -EINVAL;

	if (phba->cfg_ras_fwlog_buffsize == val)
		return 0;

	if (phba->cfg_ras_fwlog_func == PCI_FUNC(phba->pcidev->devfn))
		return -EINVAL;

	spin_lock_irq(&phba->hbalock);
	state = phba->ras_fwlog.state;
	spin_unlock_irq(&phba->hbalock);

	if (state == REG_INPROGRESS) {
		lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "6147 RAS Logging "
				"registration is in progress\n");
		return -EBUSY;
	}

	/* For disable logging: stop the logs and free the DMA.
	 * For ras_fwlog_buffsize size change we still need to free and
	 * reallocate the DMA in lpfc_sli4_ras_fwlog_init.
	 */
	phba->cfg_ras_fwlog_buffsize = val;
	if (state == ACTIVE) {
		lpfc_ras_stop_fwlog(phba);
		lpfc_sli4_ras_dma_free(phba);
	}

	lpfc_sli4_ras_init(phba);
	if (phba->ras_fwlog.ras_enabled)
		ret = lpfc_sli4_ras_fwlog_init(phba, phba->cfg_ras_fwlog_level,
					       LPFC_RAS_ENABLE_LOGGING);
	return ret;
}

lpfc_param_store(ras_fwlog_buffsize);
static DEVICE_ATTR_RW(lpfc_ras_fwlog_buffsize);

/*
 * lpfc_ras_fwlog_level: Firmware logging verbosity level
+14 −4
Original line number Diff line number Diff line
@@ -5435,10 +5435,12 @@ lpfc_bsg_get_ras_config(struct bsg_job *job)
		bsg_reply->reply_data.vendor_reply.vendor_rsp;

	/* Current logging state */
	if (ras_fwlog->ras_active == true)
	spin_lock_irq(&phba->hbalock);
	if (ras_fwlog->state == ACTIVE)
		ras_reply->state = LPFC_RASLOG_STATE_RUNNING;
	else
		ras_reply->state = LPFC_RASLOG_STATE_STOPPED;
	spin_unlock_irq(&phba->hbalock);

	ras_reply->log_level = phba->ras_fwlog.fw_loglevel;
	ras_reply->log_buff_sz = phba->cfg_ras_fwlog_buffsize;
@@ -5495,10 +5497,13 @@ lpfc_bsg_set_ras_config(struct bsg_job *job)

	if (action == LPFC_RASACTION_STOP_LOGGING) {
		/* Check if already disabled */
		if (ras_fwlog->ras_active == false) {
		spin_lock_irq(&phba->hbalock);
		if (ras_fwlog->state != ACTIVE) {
			spin_unlock_irq(&phba->hbalock);
			rc = -ESRCH;
			goto ras_job_error;
		}
		spin_unlock_irq(&phba->hbalock);

		/* Disable logging */
		lpfc_ras_stop_fwlog(phba);
@@ -5509,8 +5514,10 @@ lpfc_bsg_set_ras_config(struct bsg_job *job)
		 * FW-logging with new log-level. Return status
		 * "Logging already Running" to caller.
		 **/
		if (ras_fwlog->ras_active)
		spin_lock_irq(&phba->hbalock);
		if (ras_fwlog->state != INACTIVE)
			action_status = -EINPROGRESS;
		spin_unlock_irq(&phba->hbalock);

		/* Enable logging */
		rc = lpfc_sli4_ras_fwlog_init(phba, log_level,
@@ -5626,10 +5633,13 @@ lpfc_bsg_get_ras_fwlog(struct bsg_job *job)
		goto ras_job_error;

	/* Logging to be stopped before reading */
	if (ras_fwlog->ras_active == true) {
	spin_lock_irq(&phba->hbalock);
	if (ras_fwlog->state == ACTIVE) {
		spin_unlock_irq(&phba->hbalock);
		rc = -EINPROGRESS;
		goto ras_job_error;
	}
	spin_unlock_irq(&phba->hbalock);

	if (job->request_len <
	    sizeof(struct fc_bsg_request) +
+116 −1
Original line number Diff line number Diff line
@@ -2078,6 +2078,96 @@ lpfc_debugfs_lockstat_write(struct file *file, const char __user *buf,
}
#endif

int
lpfc_debugfs_ras_log_data(struct lpfc_hba *phba, char *buffer, int size)
{
	int copied = 0;
	struct lpfc_dmabuf *dmabuf, *next;

	spin_lock_irq(&phba->hbalock);
	if (phba->ras_fwlog.state != ACTIVE) {
		spin_unlock_irq(&phba->hbalock);
		return -EINVAL;
	}
	spin_unlock_irq(&phba->hbalock);

	list_for_each_entry_safe(dmabuf, next,
				 &phba->ras_fwlog.fwlog_buff_list, list) {
		memcpy(buffer + copied, dmabuf->virt, LPFC_RAS_MAX_ENTRY_SIZE);
		copied += LPFC_RAS_MAX_ENTRY_SIZE;
		if (size > copied)
			break;
	}
	return copied;
}

static int
lpfc_debugfs_ras_log_release(struct inode *inode, struct file *file)
{
	struct lpfc_debug *debug = file->private_data;

	vfree(debug->buffer);
	kfree(debug);

	return 0;
}

/**
 * lpfc_debugfs_ras_log_open - Open the RAS log debugfs buffer
 * @inode: The inode pointer that contains a vport pointer.
 * @file: The file pointer to attach the log output.
 *
 * Description:
 * This routine is the entry point for the debugfs open file operation. It gets
 * the vport from the i_private field in @inode, allocates the necessary buffer
 * for the log, fills the buffer from the in-memory log for this vport, and then
 * returns a pointer to that log in the private_data field in @file.
 *
 * Returns:
 * This function returns zero if successful. On error it will return a negative
 * error value.
 **/
static int
lpfc_debugfs_ras_log_open(struct inode *inode, struct file *file)
{
	struct lpfc_hba *phba = inode->i_private;
	struct lpfc_debug *debug;
	int size;
	int rc = -ENOMEM;

	spin_lock_irq(&phba->hbalock);
	if (phba->ras_fwlog.state != ACTIVE) {
		spin_unlock_irq(&phba->hbalock);
		rc = -EINVAL;
		goto out;
	}
	spin_unlock_irq(&phba->hbalock);
	debug = kmalloc(sizeof(*debug), GFP_KERNEL);
	if (!debug)
		goto out;

	size = LPFC_RAS_MIN_BUFF_POST_SIZE * phba->cfg_ras_fwlog_buffsize;
	debug->buffer = vmalloc(size);
	if (!debug->buffer)
		goto free_debug;

	debug->len = lpfc_debugfs_ras_log_data(phba, debug->buffer, size);
	if (debug->len < 0) {
		rc = -EINVAL;
		goto free_buffer;
	}
	file->private_data = debug;

	return 0;

free_buffer:
	vfree(debug->buffer);
free_debug:
	kfree(debug);
out:
	return rc;
}

/**
 * lpfc_debugfs_dumpHBASlim_open - Open the Dump HBA SLIM debugfs buffer
 * @inode: The inode pointer that contains a vport pointer.
@@ -5286,6 +5376,16 @@ static const struct file_operations lpfc_debugfs_op_lockstat = {
};
#endif

#undef lpfc_debugfs_ras_log
static const struct file_operations lpfc_debugfs_ras_log = {
	.owner =        THIS_MODULE,
	.open =         lpfc_debugfs_ras_log_open,
	.llseek =       lpfc_debugfs_lseek,
	.read =         lpfc_debugfs_read,
	.release =      lpfc_debugfs_ras_log_release,
};
#endif

#undef lpfc_debugfs_op_dumpHBASlim
static const struct file_operations lpfc_debugfs_op_dumpHBASlim = {
	.owner =        THIS_MODULE,
@@ -5457,7 +5557,6 @@ static const struct file_operations lpfc_idiag_op_extAcc = {
	.release =      lpfc_idiag_cmd_release,
};

#endif

/* lpfc_idiag_mbxacc_dump_bsg_mbox - idiag debugfs dump bsg mailbox command
 * @phba: Pointer to HBA context object.
@@ -5707,6 +5806,19 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
			goto debug_failed;
		}

		/* RAS log */
		snprintf(name, sizeof(name), "ras_log");
		phba->debug_ras_log =
			debugfs_create_file(name, 0644,
					    phba->hba_debugfs_root,
					    phba, &lpfc_debugfs_ras_log);
		if (!phba->debug_ras_log) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
					 "6148 Cannot create debugfs"
					 " ras_log\n");
			goto debug_failed;
		}

		/* Setup hbqinfo */
		snprintf(name, sizeof(name), "hbqinfo");
		phba->debug_hbqinfo =
@@ -6117,6 +6229,9 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
		debugfs_remove(phba->debug_hbqinfo); /* hbqinfo */
		phba->debug_hbqinfo = NULL;

		debugfs_remove(phba->debug_ras_log);
		phba->debug_ras_log = NULL;

#ifdef LPFC_HDWQ_LOCK_STAT
		debugfs_remove(phba->debug_lockstat); /* lockstat */
		phba->debug_lockstat = NULL;
+19 −3
Original line number Diff line number Diff line
@@ -6219,11 +6219,16 @@ lpfc_ras_stop_fwlog(struct lpfc_hba *phba)
{
	struct lpfc_ras_fwlog *ras_fwlog = &phba->ras_fwlog;
	ras_fwlog->ras_active = false;
	spin_lock_irq(&phba->hbalock);
	ras_fwlog->state = INACTIVE;
	spin_unlock_irq(&phba->hbalock);
	/* Disable FW logging to host memory */
	writel(LPFC_CTL_PDEV_CTL_DDL_RAS,
	       phba->sli4_hba.conf_regs_memmap_p + LPFC_CTL_PDEV_CTL_OFFSET);
	/* Wait 10ms for firmware to stop using DMA buffer */
	usleep_range(10 * 1000, 20 * 1000);
}
/**
@@ -6259,7 +6264,9 @@ lpfc_sli4_ras_dma_free(struct lpfc_hba *phba)
		ras_fwlog->lwpd.virt = NULL;
	}
	ras_fwlog->ras_active = false;
	spin_lock_irq(&phba->hbalock);
	ras_fwlog->state = INACTIVE;
	spin_unlock_irq(&phba->hbalock);
}
/**
@@ -6361,7 +6368,9 @@ lpfc_sli4_ras_mbox_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
		goto disable_ras;
	}
	ras_fwlog->ras_active = true;
	spin_lock_irq(&phba->hbalock);
	ras_fwlog->state = ACTIVE;
	spin_unlock_irq(&phba->hbalock);
	mempool_free(pmb, phba->mbox_mem_pool);
	return;
@@ -6393,6 +6402,10 @@ lpfc_sli4_ras_fwlog_init(struct lpfc_hba *phba,
	uint32_t len = 0, fwlog_buffsize, fwlog_entry_count;
	int rc = 0;
	spin_lock_irq(&phba->hbalock);
	ras_fwlog->state = INACTIVE;
	spin_unlock_irq(&phba->hbalock);
	fwlog_buffsize = (LPFC_RAS_MIN_BUFF_POST_SIZE *
			  phba->cfg_ras_fwlog_buffsize);
	fwlog_entry_count = (fwlog_buffsize/LPFC_RAS_MAX_ENTRY_SIZE);
@@ -6452,6 +6465,9 @@ lpfc_sli4_ras_fwlog_init(struct lpfc_hba *phba,
	mbx_fwlog->u.request.lwpd.addr_lo = putPaddrLow(ras_fwlog->lwpd.phys);
	mbx_fwlog->u.request.lwpd.addr_hi = putPaddrHigh(ras_fwlog->lwpd.phys);
	spin_lock_irq(&phba->hbalock);
	ras_fwlog->state = REG_INPROGRESS;
	spin_unlock_irq(&phba->hbalock);
	mbox->vport = phba->pport;
	mbox->mbox_cmpl = lpfc_sli4_ras_mbox_cmpl;