mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-04 04:06:26 +00:00
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: Dick Kennedy <dick.kennedy@broadcom.com> Signed-off-by: James Smart <jsmart2021@gmail.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
8156d378c4
commit
95bfc6d8ad
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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) +
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user