Commit fc444494 authored by Ranjan Kumar's avatar Ranjan Kumar Committed by Martin K. Petersen

scsi: mpi3mr: HDB allocation and posting for hardware and firmware buffers

To be able to debug controller problems it is beneficial to allocate and
configure system/host memory buffers which can be used to capture hardware
and firmware diagnostic information.

Add functions required to allocate and post firmware and hardware
diagnostic buffers to the controller and to set up automatic diagnostic
capture triggers.

Captures will be triggered under the following circumstances:

 1. Firmware is in FAULT state.

 2. Admin commands time out.

 3. Controller reset caused due to I/O timeout
Reported-by: default avatarkernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202405151758.7xrJz6rp-lkp@intel.com/Co-developed-by: default avatarSathya Prakash <sathya.prakash@broadcom.com>
Signed-off-by: default avatarSathya Prakash <sathya.prakash@broadcom.com>
Signed-off-by: default avatarRanjan Kumar <ranjan.kumar@broadcom.com>
Link: https://lore.kernel.org/r/20240626102646.14298-2-ranjan.kumar@broadcom.comSigned-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 1613e604
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2016-2024 Broadcom Inc. All rights reserved.
*/
#ifndef MPI30_TOOL_H
#define MPI30_TOOL_H 1
#define MPI3_DIAG_BUFFER_TYPE_TRACE (0x01)
#define MPI3_DIAG_BUFFER_TYPE_FW (0x02)
#define MPI3_DIAG_BUFFER_ACTION_RELEASE (0x01)
struct mpi3_diag_buffer_post_request {
__le16 host_tag;
u8 ioc_use_only02;
u8 function;
__le16 ioc_use_only04;
u8 ioc_use_only06;
u8 msg_flags;
__le16 change_count;
__le16 reserved0a;
u8 type;
u8 reserved0d;
__le16 reserved0e;
__le64 address;
__le32 length;
__le32 reserved1c;
};
struct mpi3_diag_buffer_manage_request {
__le16 host_tag;
u8 ioc_use_only02;
u8 function;
__le16 ioc_use_only04;
u8 ioc_use_only06;
u8 msg_flags;
__le16 change_count;
__le16 reserved0a;
u8 type;
u8 action;
__le16 reserved0e;
};
#endif
......@@ -47,6 +47,7 @@
#include "mpi/mpi30_ioc.h"
#include "mpi/mpi30_sas.h"
#include "mpi/mpi30_pci.h"
#include "mpi/mpi30_tool.h"
#include "mpi3mr_debug.h"
/* Global list and lock for storing multiple adapters managed by the driver */
......@@ -187,6 +188,13 @@ extern atomic64_t event_counter;
#define MPI3MR_HARD_SECURE_DEVICE 0x08
#define MPI3MR_TAMPERED_DEVICE 0x0C
#define MPI3MR_DEFAULT_HDB_MAX_SZ (4 * 1024 * 1024)
#define MPI3MR_DEFAULT_HDB_DEC_SZ (1 * 1024 * 1024)
#define MPI3MR_DEFAULT_HDB_MIN_SZ (2 * 1024 * 1024)
#define MPI3MR_MAX_NUM_HDB 2
#define MPI3MR_HDB_TRIGGER_TYPE_GLOBAL 3
/* SGE Flag definition */
#define MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST \
(MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE | MPI3_SGE_FLAGS_DLAS_SYSTEM | \
......@@ -210,6 +218,7 @@ extern atomic64_t event_counter;
#define MPI3MR_WRITE_SAME_MAX_LEN_256_BLKS 256
#define MPI3MR_WRITE_SAME_MAX_LEN_2048_BLKS 2048
/**
* struct mpi3mr_nvme_pt_sge - Structure to store SGEs for NVMe
* Encapsulated commands.
......@@ -289,6 +298,8 @@ enum mpi3mr_reset_reason {
MPI3MR_RESET_FROM_PELABORT_TIMEOUT = 22,
MPI3MR_RESET_FROM_SYSFS = 23,
MPI3MR_RESET_FROM_SYSFS_TIMEOUT = 24,
MPI3MR_RESET_FROM_DIAG_BUFFER_POST_TIMEOUT = 25,
MPI3MR_RESET_FROM_DIAG_BUFFER_RELEASE_TIMEOUT = 26,
MPI3MR_RESET_FROM_FIRMWARE = 27,
MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT = 29,
MPI3MR_RESET_FROM_SAS_TRANSPORT_TIMEOUT = 30,
......@@ -327,6 +338,9 @@ struct mpi3mr_ioc_facts {
u32 ioc_capabilities;
struct mpi3mr_compimg_ver fw_ver;
u32 mpi_version;
u32 diag_trace_sz;
u32 diag_fw_sz;
u32 diag_drvr_sz;
u16 max_reqs;
u16 product_id;
u16 op_req_sz;
......@@ -852,6 +866,41 @@ struct mpi3mr_drv_cmd {
struct mpi3mr_drv_cmd *drv_cmd);
};
/**
* union mpi3mr_trigger_data - Trigger data information
* @fault: Fault code
* @global: Global trigger data
* @element: element trigger data
*/
union mpi3mr_trigger_data {
u16 fault;
u64 global;
union mpi3_driver2_trigger_element element;
};
/**
* struct diag_buffer_desc - memory descriptor structure to
* store virtual, dma addresses, size, buffer status for host
* diagnostic buffers.
*
* @type: Buffer type
* @trigger_data: Trigger data
* @trigger_type: Trigger type
* @status: Buffer status
* @size: Buffer size
* @addr: Virtual address
* @dma_addr: Buffer DMA address
*/
struct diag_buffer_desc {
u8 type;
union mpi3mr_trigger_data trigger_data;
u8 trigger_type;
u8 status;
u32 size;
void *addr;
dma_addr_t dma_addr;
};
/**
* struct dma_memory_desc - memory descriptor structure to store
* virtual address, dma address and size for any generic dma
......@@ -1054,6 +1103,11 @@ struct scmd_priv {
* @sas_node_lock: Lock to protect SAS node list
* @hba_port_table_list: List of HBA Ports
* @enclosure_list: List of Enclosure objects
* @diag_buffers: Host diagnostic buffers
* @driver_pg2: Driver page 2 pointer
* @reply_trigger_present: Reply trigger present flag
* @event_trigger_present: Event trigger present flag
* @scsisense_trigger_present: Scsi sense trigger present flag
* @ioctl_dma_pool: DMA pool for IOCTL data buffers
* @ioctl_sge: DMA buffer descriptors for IOCTL data
* @ioctl_chain_sge: DMA buffer descriptor for IOCTL chain
......@@ -1250,6 +1304,12 @@ struct mpi3mr_ioc {
struct dma_memory_desc ioctl_chain_sge;
struct dma_memory_desc ioctl_resp_sge;
bool ioctl_sges_allocated;
bool reply_trigger_present;
bool event_trigger_present;
bool scsisense_trigger_present;
struct diag_buffer_desc diag_buffers[MPI3MR_MAX_NUM_HDB];
struct mpi3_driver_page2 *driver_pg2;
spinlock_t trigger_lock;
};
/**
......@@ -1406,6 +1466,8 @@ int mpi3mr_cfg_set_sas_io_unit_pg1(struct mpi3mr_ioc *mrioc,
struct mpi3_sas_io_unit_page1 *sas_io_unit_pg1, u16 pg_sz);
int mpi3mr_cfg_get_driver_pg1(struct mpi3mr_ioc *mrioc,
struct mpi3_driver_page1 *driver_pg1, u16 pg_sz);
int mpi3mr_cfg_get_driver_pg2(struct mpi3mr_ioc *mrioc,
struct mpi3_driver_page2 *driver_pg2, u16 pg_sz, u8 page_type);
u8 mpi3mr_is_expander_device(u16 device_info);
int mpi3mr_expander_add(struct mpi3mr_ioc *mrioc, u16 handle);
......@@ -1439,4 +1501,16 @@ void mpi3mr_free_enclosure_list(struct mpi3mr_ioc *mrioc);
int mpi3mr_process_admin_reply_q(struct mpi3mr_ioc *mrioc);
void mpi3mr_expander_node_remove(struct mpi3mr_ioc *mrioc,
struct mpi3mr_sas_node *sas_expander);
void mpi3mr_alloc_diag_bufs(struct mpi3mr_ioc *mrioc);
int mpi3mr_post_diag_bufs(struct mpi3mr_ioc *mrioc);
int mpi3mr_issue_diag_buf_release(struct mpi3mr_ioc *mrioc,
struct diag_buffer_desc *diag_buffer);
void mpi3mr_release_diag_bufs(struct mpi3mr_ioc *mrioc, u8 skip_rel_action);
void mpi3mr_set_trigger_data_in_hdb(struct diag_buffer_desc *hdb,
u8 type, union mpi3mr_trigger_data *trigger_data, bool force);
int mpi3mr_refresh_trigger(struct mpi3mr_ioc *mrioc, u8 page_type);
struct diag_buffer_desc *mpi3mr_diag_buffer_for_type(struct mpi3mr_ioc *mrioc,
u8 buf_type);
int mpi3mr_issue_diag_buf_post(struct mpi3mr_ioc *mrioc,
struct diag_buffer_desc *diag_buffer);
#endif /*MPI3MR_H_INCLUDED*/
This diff is collapsed.
......@@ -3003,7 +3003,11 @@ static void mpi3mr_process_factsdata(struct mpi3mr_ioc *mrioc,
mrioc->facts.sge_mod_shift = facts_data->sge_modifier_shift;
mrioc->facts.shutdown_timeout =
le16_to_cpu(facts_data->shutdown_timeout);
mrioc->facts.diag_trace_sz =
le32_to_cpu(facts_data->diag_trace_size);
mrioc->facts.diag_fw_sz =
le32_to_cpu(facts_data->diag_fw_size);
mrioc->facts.diag_drvr_sz = le32_to_cpu(facts_data->diag_driver_size);
mrioc->facts.max_dev_per_tg =
facts_data->max_devices_per_throttle_group;
mrioc->facts.io_throttle_data_length =
......@@ -3681,6 +3685,94 @@ static const struct {
{ MPI3_IOCFACTS_CAPABILITY_MULTIPATH_SUPPORTED, "MultiPath" },
};
/**
* mpi3mr_repost_diag_bufs - repost host diag buffers
* @mrioc: Adapter instance reference
*
* repost firmware and trace diag buffers based on global
* trigger flag from driver page 2
*
* Return: 0 on success, non-zero on failures.
*/
static int mpi3mr_repost_diag_bufs(struct mpi3mr_ioc *mrioc)
{
u64 global_trigger;
union mpi3mr_trigger_data prev_trigger_data;
struct diag_buffer_desc *trace_hdb = NULL;
struct diag_buffer_desc *fw_hdb = NULL;
int retval = 0;
bool trace_repost_needed = false;
bool fw_repost_needed = false;
u8 prev_trigger_type;
retval = mpi3mr_refresh_trigger(mrioc, MPI3_CONFIG_ACTION_READ_CURRENT);
if (retval)
return -1;
trace_hdb = mpi3mr_diag_buffer_for_type(mrioc,
MPI3_DIAG_BUFFER_TYPE_TRACE);
if (trace_hdb &&
trace_hdb->status != MPI3MR_HDB_BUFSTATUS_NOT_ALLOCATED &&
trace_hdb->trigger_type != MPI3MR_HDB_TRIGGER_TYPE_GLOBAL &&
trace_hdb->trigger_type != MPI3MR_HDB_TRIGGER_TYPE_ELEMENT)
trace_repost_needed = true;
fw_hdb = mpi3mr_diag_buffer_for_type(mrioc, MPI3_DIAG_BUFFER_TYPE_FW);
if (fw_hdb && fw_hdb->status != MPI3MR_HDB_BUFSTATUS_NOT_ALLOCATED &&
fw_hdb->trigger_type != MPI3MR_HDB_TRIGGER_TYPE_GLOBAL &&
fw_hdb->trigger_type != MPI3MR_HDB_TRIGGER_TYPE_ELEMENT)
fw_repost_needed = true;
if (trace_repost_needed || fw_repost_needed) {
global_trigger = le64_to_cpu(mrioc->driver_pg2->global_trigger);
if (global_trigger &
MPI3_DRIVER2_GLOBALTRIGGER_POST_DIAG_TRACE_DISABLED)
trace_repost_needed = false;
if (global_trigger &
MPI3_DRIVER2_GLOBALTRIGGER_POST_DIAG_FW_DISABLED)
fw_repost_needed = false;
}
if (trace_repost_needed) {
prev_trigger_type = trace_hdb->trigger_type;
memcpy(&prev_trigger_data, &trace_hdb->trigger_data,
sizeof(trace_hdb->trigger_data));
retval = mpi3mr_issue_diag_buf_post(mrioc, trace_hdb);
if (!retval) {
dprint_init(mrioc, "trace diag buffer reposted");
mpi3mr_set_trigger_data_in_hdb(trace_hdb,
MPI3MR_HDB_TRIGGER_TYPE_UNKNOWN, NULL, 1);
} else {
trace_hdb->trigger_type = prev_trigger_type;
memcpy(&trace_hdb->trigger_data, &prev_trigger_data,
sizeof(prev_trigger_data));
ioc_err(mrioc, "trace diag buffer repost failed");
return -1;
}
}
if (fw_repost_needed) {
prev_trigger_type = fw_hdb->trigger_type;
memcpy(&prev_trigger_data, &fw_hdb->trigger_data,
sizeof(fw_hdb->trigger_data));
retval = mpi3mr_issue_diag_buf_post(mrioc, fw_hdb);
if (!retval) {
dprint_init(mrioc, "firmware diag buffer reposted");
mpi3mr_set_trigger_data_in_hdb(fw_hdb,
MPI3MR_HDB_TRIGGER_TYPE_UNKNOWN, NULL, 1);
} else {
fw_hdb->trigger_type = prev_trigger_type;
memcpy(&fw_hdb->trigger_data, &prev_trigger_data,
sizeof(prev_trigger_data));
ioc_err(mrioc, "firmware diag buffer repost failed");
return -1;
}
}
return retval;
}
/**
* mpi3mr_print_ioc_info - Display controller information
* @mrioc: Adapter instance reference
......@@ -3989,9 +4081,18 @@ int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc)
}
}
dprint_init(mrioc, "allocating host diag buffers\n");
mpi3mr_alloc_diag_bufs(mrioc);
dprint_init(mrioc, "allocating ioctl dma buffers\n");
mpi3mr_alloc_ioctl_dma_memory(mrioc);
dprint_init(mrioc, "posting host diag buffers\n");
retval = mpi3mr_post_diag_bufs(mrioc);
if (retval)
ioc_warn(mrioc, "failed to post host diag buffers\n");
if (!mrioc->init_cmds.reply) {
retval = mpi3mr_alloc_reply_sense_bufs(mrioc);
if (retval) {
......@@ -4144,6 +4245,17 @@ int mpi3mr_reinit_ioc(struct mpi3mr_ioc *mrioc, u8 is_resume)
mpi3mr_print_ioc_info(mrioc);
if (is_resume) {
dprint_reset(mrioc, "posting host diag buffers\n");
retval = mpi3mr_post_diag_bufs(mrioc);
if (retval)
ioc_warn(mrioc, "failed to post host diag buffers\n");
} else {
retval = mpi3mr_repost_diag_bufs(mrioc);
if (retval)
ioc_warn(mrioc, "failed to re post host diag buffers\n");
}
dprint_reset(mrioc, "sending ioc_init\n");
retval = mpi3mr_issue_iocinit(mrioc);
if (retval) {
......@@ -4409,6 +4521,7 @@ void mpi3mr_free_mem(struct mpi3mr_ioc *mrioc)
{
u16 i;
struct mpi3mr_intr_info *intr_info;
struct diag_buffer_desc *diag_buffer;
mpi3mr_free_enclosure_list(mrioc);
mpi3mr_free_ioctl_dma_memory(mrioc);
......@@ -4543,6 +4656,19 @@ void mpi3mr_free_mem(struct mpi3mr_ioc *mrioc)
mrioc->pel_seqnum_virt = NULL;
}
for (i = 0; i < MPI3MR_MAX_NUM_HDB; i++) {
diag_buffer = &mrioc->diag_buffers[i];
if (diag_buffer->addr) {
dma_free_coherent(&mrioc->pdev->dev,
diag_buffer->size, diag_buffer->addr,
diag_buffer->dma_addr);
diag_buffer->addr = NULL;
diag_buffer->size = 0;
diag_buffer->type = 0;
diag_buffer->status = 0;
}
}
kfree(mrioc->throttle_groups);
mrioc->throttle_groups = NULL;
......@@ -5016,6 +5142,9 @@ int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc,
if ((!snapdump) && (reset_reason != MPI3MR_RESET_FROM_FAULT_WATCH) &&
(reset_reason != MPI3MR_RESET_FROM_FIRMWARE) &&
(reset_reason != MPI3MR_RESET_FROM_CIACTIV_FAULT)) {
dprint_reset(mrioc,
"soft_reset_handler: releasing host diagnostic buffers\n");
mpi3mr_release_diag_bufs(mrioc, 0);
for (i = 0; i < MPI3_EVENT_NOTIFY_EVENTMASK_WORDS; i++)
mrioc->event_masks[i] = -1;
......@@ -5075,6 +5204,7 @@ int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc,
mrioc->prepare_for_reset_timeout_counter = 0;
}
mpi3mr_memset_buffers(mrioc);
mpi3mr_release_diag_bufs(mrioc, 1);
retval = mpi3mr_reinit_ioc(mrioc, 0);
if (retval) {
pr_err(IOCNAME "reinit after soft reset failed: reason %d\n",
......@@ -5954,3 +6084,64 @@ int mpi3mr_cfg_get_driver_pg1(struct mpi3mr_ioc *mrioc,
out_failed:
return -1;
}
/**
* mpi3mr_cfg_get_driver_pg2 - Read current driver page2
* @mrioc: Adapter instance reference
* @driver_pg2: Pointer to return driver page 2
* @pg_sz: Size of the memory allocated to the page pointer
* @page_action: Page action
*
* This is handler for config page read for the driver page2.
* This routine checks ioc_status to decide whether the page
* read is success or not.
*
* Return: 0 on success, non-zero on failure.
*/
int mpi3mr_cfg_get_driver_pg2(struct mpi3mr_ioc *mrioc,
struct mpi3_driver_page2 *driver_pg2, u16 pg_sz, u8 page_action)
{
struct mpi3_config_page_header cfg_hdr;
struct mpi3_config_request cfg_req;
u16 ioc_status = 0;
memset(driver_pg2, 0, pg_sz);
memset(&cfg_hdr, 0, sizeof(cfg_hdr));
memset(&cfg_req, 0, sizeof(cfg_req));
cfg_req.function = MPI3_FUNCTION_CONFIG;
cfg_req.action = MPI3_CONFIG_ACTION_PAGE_HEADER;
cfg_req.page_type = MPI3_CONFIG_PAGETYPE_DRIVER;
cfg_req.page_number = 2;
cfg_req.page_address = 0;
cfg_req.page_version = MPI3_DRIVER2_PAGEVERSION;
if (mpi3mr_process_cfg_req(mrioc, &cfg_req, NULL,
MPI3MR_INTADMCMD_TIMEOUT, &ioc_status, &cfg_hdr, sizeof(cfg_hdr))) {
ioc_err(mrioc, "driver page2 header read failed\n");
goto out_failed;
}
if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
ioc_err(mrioc, "driver page2 header read failed with\n"
"ioc_status(0x%04x)\n",
ioc_status);
goto out_failed;
}
cfg_req.action = page_action;
if (mpi3mr_process_cfg_req(mrioc, &cfg_req, &cfg_hdr,
MPI3MR_INTADMCMD_TIMEOUT, &ioc_status, driver_pg2, pg_sz)) {
ioc_err(mrioc, "driver page2 read failed\n");
goto out_failed;
}
if (ioc_status != MPI3_IOCSTATUS_SUCCESS) {
ioc_err(mrioc, "driver page2 read failed with\n"
"ioc_status(0x%04x)\n",
ioc_status);
goto out_failed;
}
return 0;
out_failed:
return -1;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment