aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/scsi/mpi3mr/mpi/mpi30_tool.h44
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr.h133
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_app.c1080
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_fw.c272
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_os.c113
-rw-r--r--include/uapi/scsi/scsi_bsg_mpi3mr.h3
6 files changed, 1634 insertions, 11 deletions
diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_tool.h b/drivers/scsi/mpi3mr/mpi/mpi30_tool.h
new file mode 100644
index 000000000000..3b960893870f
--- /dev/null
+++ b/drivers/scsi/mpi3mr/mpi/mpi30_tool.h
@@ -0,0 +1,44 @@
+/* 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
diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h
index f5a1529fa537..c8968f12b9e6 100644
--- a/drivers/scsi/mpi3mr/mpi3mr.h
+++ b/drivers/scsi/mpi3mr/mpi3mr.h
@@ -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 */
@@ -55,8 +56,8 @@ extern struct list_head mrioc_list;
extern int prot_mask;
extern atomic64_t event_counter;
-#define MPI3MR_DRIVER_VERSION "8.8.1.0.50"
-#define MPI3MR_DRIVER_RELDATE "5-March-2024"
+#define MPI3MR_DRIVER_VERSION "8.9.1.0.50"
+#define MPI3MR_DRIVER_RELDATE "14-May-2024"
#define MPI3MR_DRIVER_NAME "mpi3mr"
#define MPI3MR_DRIVER_LICENSE "GPL"
@@ -187,6 +188,30 @@ 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_UNKNOWN 0
+#define MPI3MR_HDB_TRIGGER_TYPE_FAULT 1
+#define MPI3MR_HDB_TRIGGER_TYPE_ELEMENT 2
+#define MPI3MR_HDB_TRIGGER_TYPE_GLOBAL 3
+#define MPI3MR_HDB_TRIGGER_TYPE_SOFT_RESET 4
+#define MPI3MR_HDB_TRIGGER_TYPE_FW_RELEASED 5
+
+#define MPI3MR_HDB_REFRESH_TYPE_RESERVED 0
+#define MPI3MR_HDB_REFRESH_TYPE_CURRENT 1
+#define MPI3MR_HDB_REFRESH_TYPE_DEFAULT 2
+#define MPI3MR_HDB_HDB_REFRESH_TYPE_PERSISTENT 3
+
+#define MPI3MR_DEFAULT_HDB_SZ (4 * 1024 * 1024)
+#define MPI3MR_MAX_NUM_HDB 2
+
+#define MPI3MR_HDB_QUERY_ELEMENT_TRIGGER_FORMAT_INDEX 0
+#define MPI3MR_HDB_QUERY_ELEMENT_TRIGGER_FORMAT_DATA 1
+
+
/* SGE Flag definition */
#define MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST \
(MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE | MPI3_SGE_FLAGS_DLAS_SYSTEM | \
@@ -210,6 +235,8 @@ extern atomic64_t event_counter;
#define MPI3MR_WRITE_SAME_MAX_LEN_256_BLKS 256
#define MPI3MR_WRITE_SAME_MAX_LEN_2048_BLKS 2048
+#define MPI3MR_DRIVER_EVENT_PROCESS_TRIGGER (0xFFFD)
+
/**
* struct mpi3mr_nvme_pt_sge - Structure to store SGEs for NVMe
* Encapsulated commands.
@@ -289,9 +316,12 @@ 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,
+ MPI3MR_RESET_FROM_TRIGGER = 31,
};
#define MPI3MR_RESET_REASON_OSTYPE_LINUX 1
@@ -327,6 +357,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;
@@ -853,6 +886,59 @@ struct mpi3mr_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 trigger_event_data - store trigger related
+ * information.
+ *
+ * @trace_hdb: Trace diag buffer descriptor reference
+ * @fw_hdb: FW diag buffer descriptor reference
+ * @trigger_type: Trigger type
+ * @trigger_specific_data: Trigger specific data
+ * @snapdump: Snapdump enable or disable flag
+ */
+struct trigger_event_data {
+ struct diag_buffer_desc *trace_hdb;
+ struct diag_buffer_desc *fw_hdb;
+ u8 trigger_type;
+ union mpi3mr_trigger_data trigger_specific_data;
+ bool snapdump;
+};
+
+/**
+ * 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
* memory allocations in the driver.
@@ -1054,11 +1140,19 @@ 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
* @ioctl_resp_sge: DMA buffer descriptor for Mgmt cmd response
* @ioctl_sges_allocated: Flag for IOCTL SGEs allocated or not
+ * @trace_release_trigger_active: Trace trigger active flag
+ * @fw_release_trigger_active: Fw release trigger active flag
+ * @snapdump_trigger_active: Snapdump trigger active flag
*/
struct mpi3mr_ioc {
struct list_head list;
@@ -1250,6 +1344,15 @@ 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;
+ bool snapdump_trigger_active;
+ bool trace_release_trigger_active;
+ bool fw_release_trigger_active;
};
/**
@@ -1406,6 +1509,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 +1544,28 @@ 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);
+void mpi3mr_set_trigger_data_in_all_hdb(struct mpi3mr_ioc *mrioc,
+ u8 type, union mpi3mr_trigger_data *trigger_data, bool force);
+void mpi3mr_reply_trigger(struct mpi3mr_ioc *mrioc, u16 iocstatus,
+ u32 iocloginfo);
+void mpi3mr_hdb_trigger_data_event(struct mpi3mr_ioc *mrioc,
+ struct trigger_event_data *event_data);
+void mpi3mr_scsisense_trigger(struct mpi3mr_ioc *mrioc, u8 senseky, u8 asc,
+ u8 ascq);
+void mpi3mr_event_trigger(struct mpi3mr_ioc *mrioc, u8 event);
+void mpi3mr_global_trigger(struct mpi3mr_ioc *mrioc, u64 trigger_data);
+void mpi3mr_hdbstatuschg_evt_th(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply);
#endif /*MPI3MR_H_INCLUDED*/
diff --git a/drivers/scsi/mpi3mr/mpi3mr_app.c b/drivers/scsi/mpi3mr/mpi3mr_app.c
index 1638109a68a0..f73f265c7921 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_app.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_app.c
@@ -12,6 +12,821 @@
#include <uapi/scsi/scsi_bsg_mpi3mr.h>
/**
+ * mpi3mr_alloc_trace_buffer: Allocate trace buffer
+ * @mrioc: Adapter instance reference
+ * @trace_size: Trace buffer size
+ *
+ * Allocate trace buffer
+ * Return: 0 on success, non-zero on failure.
+ */
+static int mpi3mr_alloc_trace_buffer(struct mpi3mr_ioc *mrioc, u32 trace_size)
+{
+ struct diag_buffer_desc *diag_buffer = &mrioc->diag_buffers[0];
+
+ diag_buffer->addr = dma_alloc_coherent(&mrioc->pdev->dev,
+ trace_size, &diag_buffer->dma_addr, GFP_KERNEL);
+ if (diag_buffer->addr) {
+ dprint_init(mrioc, "trace diag buffer is allocated successfully\n");
+ return 0;
+ }
+ return -1;
+}
+
+/**
+ * mpi3mr_alloc_diag_bufs - Allocate memory for diag buffers
+ * @mrioc: Adapter instance reference
+ *
+ * This functions checks whether the driver defined buffer sizes
+ * are greater than IOCFacts provided controller local buffer
+ * sizes and if the driver defined sizes are more then the
+ * driver allocates the specific buffer by reading driver page1
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_alloc_diag_bufs(struct mpi3mr_ioc *mrioc)
+{
+ struct diag_buffer_desc *diag_buffer;
+ struct mpi3_driver_page1 driver_pg1;
+ u32 trace_dec_size, trace_min_size, fw_dec_size, fw_min_size,
+ trace_size, fw_size;
+ u16 pg_sz = sizeof(driver_pg1);
+ int retval = 0;
+ bool retry = false;
+
+ if (mrioc->diag_buffers[0].addr || mrioc->diag_buffers[1].addr)
+ return;
+
+ retval = mpi3mr_cfg_get_driver_pg1(mrioc, &driver_pg1, pg_sz);
+ if (retval) {
+ ioc_warn(mrioc,
+ "%s: driver page 1 read failed, allocating trace\n"
+ "and firmware diag buffers of default size\n", __func__);
+ trace_size = fw_size = MPI3MR_DEFAULT_HDB_MAX_SZ;
+ trace_dec_size = fw_dec_size = MPI3MR_DEFAULT_HDB_DEC_SZ;
+ trace_min_size = fw_min_size = MPI3MR_DEFAULT_HDB_MIN_SZ;
+
+ } else {
+ trace_size = driver_pg1.host_diag_trace_max_size * 1024;
+ trace_dec_size = driver_pg1.host_diag_trace_decrement_size
+ * 1024;
+ trace_min_size = driver_pg1.host_diag_trace_min_size * 1024;
+ fw_size = driver_pg1.host_diag_fw_max_size * 1024;
+ fw_dec_size = driver_pg1.host_diag_fw_decrement_size * 1024;
+ fw_min_size = driver_pg1.host_diag_fw_min_size * 1024;
+ dprint_init(mrioc,
+ "%s:trace diag buffer sizes read from driver\n"
+ "page1: maximum size = %dKB, decrement size = %dKB\n"
+ ", minimum size = %dKB\n", __func__, driver_pg1.host_diag_trace_max_size,
+ driver_pg1.host_diag_trace_decrement_size,
+ driver_pg1.host_diag_trace_min_size);
+ dprint_init(mrioc,
+ "%s:firmware diag buffer sizes read from driver\n"
+ "page1: maximum size = %dKB, decrement size = %dKB\n"
+ ", minimum size = %dKB\n", __func__, driver_pg1.host_diag_fw_max_size,
+ driver_pg1.host_diag_fw_decrement_size,
+ driver_pg1.host_diag_fw_min_size);
+ if ((trace_size == 0) && (fw_size == 0))
+ return;
+ }
+
+
+retry_trace:
+ diag_buffer = &mrioc->diag_buffers[0];
+ diag_buffer->type = MPI3_DIAG_BUFFER_TYPE_TRACE;
+ diag_buffer->status = MPI3MR_HDB_BUFSTATUS_NOT_ALLOCATED;
+ if ((mrioc->facts.diag_trace_sz < trace_size) && (trace_size >=
+ trace_min_size)) {
+ if (!retry)
+ dprint_init(mrioc,
+ "trying to allocate trace diag buffer of size = %dKB\n",
+ trace_size / 1024);
+ if (mpi3mr_alloc_trace_buffer(mrioc, trace_size)) {
+ retry = true;
+ trace_size -= trace_dec_size;
+ dprint_init(mrioc, "trace diag buffer allocation failed\n"
+ "retrying smaller size %dKB\n", trace_size / 1024);
+ goto retry_trace;
+ } else
+ diag_buffer->size = trace_size;
+ }
+
+ retry = false;
+retry_fw:
+
+ diag_buffer = &mrioc->diag_buffers[1];
+
+ diag_buffer->type = MPI3_DIAG_BUFFER_TYPE_FW;
+ diag_buffer->status = MPI3MR_HDB_BUFSTATUS_NOT_ALLOCATED;
+ if ((mrioc->facts.diag_fw_sz < fw_size) && (fw_size >= fw_min_size)) {
+ diag_buffer->addr = dma_alloc_coherent(&mrioc->pdev->dev,
+ fw_size, &diag_buffer->dma_addr, GFP_KERNEL);
+ if (!retry)
+ dprint_init(mrioc,
+ "%s:trying to allocate firmware diag buffer of size = %dKB\n",
+ __func__, fw_size / 1024);
+ if (diag_buffer->addr) {
+ dprint_init(mrioc, "%s:firmware diag buffer allocated successfully\n",
+ __func__);
+ diag_buffer->size = fw_size;
+ } else {
+ retry = true;
+ fw_size -= fw_dec_size;
+ dprint_init(mrioc, "%s:trace diag buffer allocation failed,\n"
+ "retrying smaller size %dKB\n",
+ __func__, fw_size / 1024);
+ goto retry_fw;
+ }
+ }
+}
+
+/**
+ * mpi3mr_issue_diag_buf_post - Send diag buffer post req
+ * @mrioc: Adapter instance reference
+ * @diag_buffer: Diagnostic buffer descriptor
+ *
+ * Issue diagnostic buffer post MPI request through admin queue
+ * and wait for the completion of it or time out.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+int mpi3mr_issue_diag_buf_post(struct mpi3mr_ioc *mrioc,
+ struct diag_buffer_desc *diag_buffer)
+{
+ struct mpi3_diag_buffer_post_request diag_buf_post_req;
+ u8 prev_status;
+ int retval = 0;
+
+ memset(&diag_buf_post_req, 0, sizeof(diag_buf_post_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ dprint_bsg_err(mrioc, "%s: command is in use\n", __func__);
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ return -1;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ diag_buf_post_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ diag_buf_post_req.function = MPI3_FUNCTION_DIAG_BUFFER_POST;
+ diag_buf_post_req.type = diag_buffer->type;
+ diag_buf_post_req.address = le64_to_cpu(diag_buffer->dma_addr);
+ diag_buf_post_req.length = le32_to_cpu(diag_buffer->size);
+
+ dprint_bsg_info(mrioc, "%s: posting diag buffer type %d\n", __func__,
+ diag_buffer->type);
+ prev_status = diag_buffer->status;
+ diag_buffer->status = MPI3MR_HDB_BUFSTATUS_POSTED_UNPAUSED;
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &diag_buf_post_req,
+ sizeof(diag_buf_post_req), 1);
+ if (retval) {
+ dprint_bsg_err(mrioc, "%s: admin request post failed\n",
+ __func__);
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ mrioc->init_cmds.is_waiting = 0;
+ dprint_bsg_err(mrioc, "%s: command timedout\n", __func__);
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_DIAG_BUFFER_POST_TIMEOUT);
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ dprint_bsg_err(mrioc,
+ "%s: command failed, buffer_type (%d) ioc_status(0x%04x) log_info(0x%08x)\n",
+ __func__, diag_buffer->type,
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+ dprint_bsg_info(mrioc, "%s: diag buffer type %d posted successfully\n",
+ __func__, diag_buffer->type);
+
+out_unlock:
+ if (retval)
+ diag_buffer->status = prev_status;
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ return retval;
+}
+
+/**
+ * mpi3mr_post_diag_bufs - Post diag buffers to the controller
+ * @mrioc: Adapter instance reference
+ *
+ * This function calls helper function to post both trace and
+ * firmware buffers to the controller.
+ *
+ * Return: None
+ */
+int mpi3mr_post_diag_bufs(struct mpi3mr_ioc *mrioc)
+{
+ u8 i;
+ struct diag_buffer_desc *diag_buffer;
+
+ for (i = 0; i < MPI3MR_MAX_NUM_HDB; i++) {
+ diag_buffer = &mrioc->diag_buffers[i];
+ if (!(diag_buffer->addr))
+ continue;
+ if (mpi3mr_issue_diag_buf_post(mrioc, diag_buffer))
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * mpi3mr_issue_diag_buf_release - Send diag buffer release req
+ * @mrioc: Adapter instance reference
+ * @diag_buffer: Diagnostic buffer descriptor
+ *
+ * Issue diagnostic buffer manage MPI request with release
+ * action request through admin queue and wait for the
+ * completion of it or time out.
+ *
+ * Return: 0 on success, non-zero on failures.
+ */
+int mpi3mr_issue_diag_buf_release(struct mpi3mr_ioc *mrioc,
+ struct diag_buffer_desc *diag_buffer)
+{
+ struct mpi3_diag_buffer_manage_request diag_buf_manage_req;
+ int retval = 0;
+
+ if ((diag_buffer->status != MPI3MR_HDB_BUFSTATUS_POSTED_UNPAUSED) &&
+ (diag_buffer->status != MPI3MR_HDB_BUFSTATUS_POSTED_PAUSED))
+ return retval;
+
+ memset(&diag_buf_manage_req, 0, sizeof(diag_buf_manage_req));
+ mutex_lock(&mrioc->init_cmds.mutex);
+ if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) {
+ dprint_reset(mrioc, "%s: command is in use\n", __func__);
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ return -1;
+ }
+ mrioc->init_cmds.state = MPI3MR_CMD_PENDING;
+ mrioc->init_cmds.is_waiting = 1;
+ mrioc->init_cmds.callback = NULL;
+ diag_buf_manage_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS);
+ diag_buf_manage_req.function = MPI3_FUNCTION_DIAG_BUFFER_MANAGE;
+ diag_buf_manage_req.type = diag_buffer->type;
+ diag_buf_manage_req.action = MPI3_DIAG_BUFFER_ACTION_RELEASE;
+
+
+ dprint_reset(mrioc, "%s: releasing diag buffer type %d\n", __func__,
+ diag_buffer->type);
+ init_completion(&mrioc->init_cmds.done);
+ retval = mpi3mr_admin_request_post(mrioc, &diag_buf_manage_req,
+ sizeof(diag_buf_manage_req), 1);
+ if (retval) {
+ dprint_reset(mrioc, "%s: admin request post failed\n", __func__);
+ mpi3mr_set_trigger_data_in_hdb(diag_buffer,
+ MPI3MR_HDB_TRIGGER_TYPE_UNKNOWN, NULL, 1);
+ goto out_unlock;
+ }
+ wait_for_completion_timeout(&mrioc->init_cmds.done,
+ (MPI3MR_INTADMCMD_TIMEOUT * HZ));
+ if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) {
+ mrioc->init_cmds.is_waiting = 0;
+ dprint_reset(mrioc, "%s: command timedout\n", __func__);
+ mpi3mr_check_rh_fault_ioc(mrioc,
+ MPI3MR_RESET_FROM_DIAG_BUFFER_RELEASE_TIMEOUT);
+ retval = -1;
+ goto out_unlock;
+ }
+ if ((mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK)
+ != MPI3_IOCSTATUS_SUCCESS) {
+ dprint_reset(mrioc,
+ "%s: command failed, buffer_type (%d) ioc_status(0x%04x) log_info(0x%08x)\n",
+ __func__, diag_buffer->type,
+ (mrioc->init_cmds.ioc_status & MPI3_IOCSTATUS_STATUS_MASK),
+ mrioc->init_cmds.ioc_loginfo);
+ retval = -1;
+ goto out_unlock;
+ }
+ dprint_reset(mrioc, "%s: diag buffer type %d released successfully\n",
+ __func__, diag_buffer->type);
+
+out_unlock:
+ mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED;
+ mutex_unlock(&mrioc->init_cmds.mutex);
+ return retval;
+}
+
+/**
+ * mpi3mr_process_trigger - Generic HDB Trigger handler
+ * @mrioc: Adapter instance reference
+ * @trigger_type: Trigger type
+ * @trigger_data: Trigger data
+ * @trigger_flags: Trigger flags
+ *
+ * This function checks validity of HDB, triggers and based on
+ * trigger information, creates an event to be processed in the
+ * firmware event worker thread .
+ *
+ * This function should be called with trigger spinlock held
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_process_trigger(struct mpi3mr_ioc *mrioc, u8 trigger_type,
+ union mpi3mr_trigger_data *trigger_data, u8 trigger_flags)
+{
+ struct trigger_event_data event_data;
+ struct diag_buffer_desc *trace_hdb = NULL;
+ struct diag_buffer_desc *fw_hdb = NULL;
+ u64 global_trigger;
+
+ trace_hdb = mpi3mr_diag_buffer_for_type(mrioc,
+ MPI3_DIAG_BUFFER_TYPE_TRACE);
+ if (trace_hdb &&
+ (trace_hdb->status != MPI3MR_HDB_BUFSTATUS_POSTED_UNPAUSED) &&
+ (trace_hdb->status != MPI3MR_HDB_BUFSTATUS_POSTED_PAUSED))
+ trace_hdb = NULL;
+
+ fw_hdb = mpi3mr_diag_buffer_for_type(mrioc, MPI3_DIAG_BUFFER_TYPE_FW);
+
+ if (fw_hdb &&
+ (fw_hdb->status != MPI3MR_HDB_BUFSTATUS_POSTED_UNPAUSED) &&
+ (fw_hdb->status != MPI3MR_HDB_BUFSTATUS_POSTED_PAUSED))
+ fw_hdb = NULL;
+
+ if (mrioc->snapdump_trigger_active || (mrioc->fw_release_trigger_active
+ && mrioc->trace_release_trigger_active) ||
+ (!trace_hdb && !fw_hdb) || (!mrioc->driver_pg2) ||
+ ((trigger_type == MPI3MR_HDB_TRIGGER_TYPE_ELEMENT)
+ && (!mrioc->driver_pg2->num_triggers)))
+ return;
+
+ memset(&event_data, 0, sizeof(event_data));
+ event_data.trigger_type = trigger_type;
+ memcpy(&event_data.trigger_specific_data, trigger_data,
+ sizeof(*trigger_data));
+ global_trigger = le64_to_cpu(mrioc->driver_pg2->global_trigger);
+
+ if (global_trigger & MPI3_DRIVER2_GLOBALTRIGGER_SNAPDUMP_ENABLED) {
+ event_data.snapdump = true;
+ event_data.trace_hdb = trace_hdb;
+ event_data.fw_hdb = fw_hdb;
+ mrioc->snapdump_trigger_active = true;
+ } else if (trigger_type == MPI3MR_HDB_TRIGGER_TYPE_GLOBAL) {
+ if ((trace_hdb) && (global_trigger &
+ MPI3_DRIVER2_GLOBALTRIGGER_DIAG_TRACE_RELEASE) &&
+ (!mrioc->trace_release_trigger_active)) {
+ event_data.trace_hdb = trace_hdb;
+ mrioc->trace_release_trigger_active = true;
+ }
+ if ((fw_hdb) && (global_trigger &
+ MPI3_DRIVER2_GLOBALTRIGGER_DIAG_FW_RELEASE) &&
+ (!mrioc->fw_release_trigger_active)) {
+ event_data.fw_hdb = fw_hdb;
+ mrioc->fw_release_trigger_active = true;
+ }
+ } else if (trigger_type == MPI3MR_HDB_TRIGGER_TYPE_ELEMENT) {
+ if ((trace_hdb) && (trigger_flags &
+ MPI3_DRIVER2_TRIGGER_FLAGS_DIAG_TRACE_RELEASE) &&
+ (!mrioc->trace_release_trigger_active)) {
+ event_data.trace_hdb = trace_hdb;
+ mrioc->trace_release_trigger_active = true;
+ }
+ if ((fw_hdb) && (trigger_flags &
+ MPI3_DRIVER2_TRIGGER_FLAGS_DIAG_FW_RELEASE) &&
+ (!mrioc->fw_release_trigger_active)) {
+ event_data.fw_hdb = fw_hdb;
+ mrioc->fw_release_trigger_active = true;
+ }
+ }
+
+ if (event_data.trace_hdb || event_data.fw_hdb)
+ mpi3mr_hdb_trigger_data_event(mrioc, &event_data);
+}
+
+/**
+ * mpi3mr_global_trigger - Global HDB trigger handler
+ * @mrioc: Adapter instance reference
+ * @trigger_data: Trigger data
+ *
+ * This function checks whether the given global trigger is
+ * enabled in the driver page 2 and if so calls generic trigger
+ * handler to queue event for HDB release.
+ *
+ * Return: Nothing
+ */
+void mpi3mr_global_trigger(struct mpi3mr_ioc *mrioc, u64 trigger_data)
+{
+ unsigned long flags;
+ union mpi3mr_trigger_data trigger_specific_data;
+
+ spin_lock_irqsave(&mrioc->trigger_lock, flags);
+ if (le64_to_cpu(mrioc->driver_pg2->global_trigger) & trigger_data) {
+ memset(&trigger_specific_data, 0,
+ sizeof(trigger_specific_data));
+ trigger_specific_data.global = trigger_data;
+ mpi3mr_process_trigger(mrioc, MPI3MR_HDB_TRIGGER_TYPE_GLOBAL,
+ &trigger_specific_data, 0);
+ }
+ spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
+}
+
+/**
+ * mpi3mr_scsisense_trigger - SCSI sense HDB trigger handler
+ * @mrioc: Adapter instance reference
+ * @sensekey: Sense Key
+ * @asc: Additional Sense Code
+ * @ascq: Additional Sense Code Qualifier
+ *
+ * This function compares SCSI sense trigger values with driver
+ * page 2 values and calls generic trigger handler to release
+ * HDBs if match found
+ *
+ * Return: Nothing
+ */
+void mpi3mr_scsisense_trigger(struct mpi3mr_ioc *mrioc, u8 sensekey, u8 asc,
+ u8 ascq)
+{
+ struct mpi3_driver2_trigger_scsi_sense *scsi_sense_trigger = NULL;
+ u64 i = 0;
+ unsigned long flags;
+ u8 num_triggers, trigger_flags;
+
+ if (mrioc->scsisense_trigger_present) {
+ spin_lock_irqsave(&mrioc->trigger_lock, flags);
+ scsi_sense_trigger = (struct mpi3_driver2_trigger_scsi_sense *)
+ mrioc->driver_pg2->trigger;
+ num_triggers = mrioc->driver_pg2->num_triggers;
+ for (i = 0; i < num_triggers; i++, scsi_sense_trigger++) {
+ if (scsi_sense_trigger->type !=
+ MPI3_DRIVER2_TRIGGER_TYPE_SCSI_SENSE)
+ continue;
+ if (!(scsi_sense_trigger->sense_key ==
+ MPI3_DRIVER2_TRIGGER_SCSI_SENSE_SENSE_KEY_MATCH_ALL
+ || scsi_sense_trigger->sense_key == sensekey))
+ continue;
+ if (!(scsi_sense_trigger->asc ==
+ MPI3_DRIVER2_TRIGGER_SCSI_SENSE_ASC_MATCH_ALL ||
+ scsi_sense_trigger->asc == asc))
+ continue;
+ if (!(scsi_sense_trigger->ascq ==
+ MPI3_DRIVER2_TRIGGER_SCSI_SENSE_ASCQ_MATCH_ALL ||
+ scsi_sense_trigger->ascq == ascq))
+ continue;
+ trigger_flags = scsi_sense_trigger->flags;
+ mpi3mr_process_trigger(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_ELEMENT,
+ (union mpi3mr_trigger_data *)scsi_sense_trigger,
+ trigger_flags);
+ break;
+ }
+ spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
+ }
+}
+
+/**
+ * mpi3mr_event_trigger - MPI event HDB trigger handler
+ * @mrioc: Adapter instance reference
+ * @event: MPI Event
+ *
+ * This function compares event trigger values with driver page
+ * 2 values and calls generic trigger handler to release
+ * HDBs if match found.
+ *
+ * Return: Nothing
+ */
+void mpi3mr_event_trigger(struct mpi3mr_ioc *mrioc, u8 event)
+{
+ struct mpi3_driver2_trigger_event *event_trigger = NULL;
+ u64 i = 0;
+ unsigned long flags;
+ u8 num_triggers, trigger_flags;
+
+ if (mrioc->event_trigger_present) {
+ spin_lock_irqsave(&mrioc->trigger_lock, flags);
+ event_trigger = (struct mpi3_driver2_trigger_event *)
+ mrioc->driver_pg2->trigger;
+ num_triggers = mrioc->driver_pg2->num_triggers;
+
+ for (i = 0; i < num_triggers; i++, event_trigger++) {
+ if (event_trigger->type !=
+ MPI3_DRIVER2_TRIGGER_TYPE_EVENT)
+ continue;
+ if (event_trigger->event != event)
+ continue;
+ trigger_flags = event_trigger->flags;
+ mpi3mr_process_trigger(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_ELEMENT,
+ (union mpi3mr_trigger_data *)event_trigger,
+ trigger_flags);
+ break;
+ }
+ spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
+ }
+}
+
+/**
+ * mpi3mr_reply_trigger - MPI Reply HDB trigger handler
+ * @mrioc: Adapter instance reference
+ * @ioc_status: Masked value of IOC Status from MPI Reply
+ * @ioc_loginfo: IOC Log Info from MPI Reply
+ *
+ * This function compares IOC status and IOC log info trigger
+ * values with driver page 2 values and calls generic trigger
+ * handler to release HDBs if match found.
+ *
+ * Return: Nothing
+ */
+void mpi3mr_reply_trigger(struct mpi3mr_ioc *mrioc, u16 ioc_status,
+ u32 ioc_loginfo)
+{
+ struct mpi3_driver2_trigger_reply *reply_trigger = NULL;
+ u64 i = 0;
+ unsigned long flags;
+ u8 num_triggers, trigger_flags;
+
+ if (mrioc->reply_trigger_present) {
+ spin_lock_irqsave(&mrioc->trigger_lock, flags);
+ reply_trigger = (struct mpi3_driver2_trigger_reply *)
+ mrioc->driver_pg2->trigger;
+ num_triggers = mrioc->driver_pg2->num_triggers;
+ for (i = 0; i < num_triggers; i++, reply_trigger++) {
+ if (reply_trigger->type !=
+ MPI3_DRIVER2_TRIGGER_TYPE_REPLY)
+ continue;
+ if ((le16_to_cpu(reply_trigger->ioc_status) !=
+ ioc_status)
+ && (le16_to_cpu(reply_trigger->ioc_status) !=
+ MPI3_DRIVER2_TRIGGER_REPLY_IOCSTATUS_MATCH_ALL))
+ continue;
+ if ((le32_to_cpu(reply_trigger->ioc_log_info) !=
+ (le32_to_cpu(reply_trigger->ioc_log_info_mask) &
+ ioc_loginfo)))
+ continue;
+ trigger_flags = reply_trigger->flags;
+ mpi3mr_process_trigger(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_ELEMENT,
+ (union mpi3mr_trigger_data *)reply_trigger,
+ trigger_flags);
+ break;
+ }
+ spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
+ }
+}
+
+/**
+ * mpi3mr_get_num_trigger - Gets number of HDB triggers
+ * @mrioc: Adapter instance reference
+ * @num_triggers: Number of triggers
+ * @page_action: Page action
+ *
+ * This function reads number of triggers by reading driver page
+ * 2
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static int mpi3mr_get_num_trigger(struct mpi3mr_ioc *mrioc, u8 *num_triggers,
+ u8 page_action)
+{
+ struct mpi3_driver_page2 drvr_page2;
+ int retval = 0;
+
+ *num_triggers = 0;
+
+ retval = mpi3mr_cfg_get_driver_pg2(mrioc, &drvr_page2,
+ sizeof(struct mpi3_driver_page2), page_action);
+
+ if (retval) {
+ dprint_init(mrioc, "%s: driver page 2 read failed\n", __func__);
+ return retval;
+ }
+ *num_triggers = drvr_page2.num_triggers;
+ return retval;
+}
+
+/**
+ * mpi3mr_refresh_trigger - Handler for Refresh trigger BSG
+ * @mrioc: Adapter instance reference
+ * @page_action: Page action
+ *
+ * This function caches the driver page 2 in the driver's memory
+ * by reading driver page 2 from the controller for a given page
+ * type and updates the HDB trigger values
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+int mpi3mr_refresh_trigger(struct mpi3mr_ioc *mrioc, u8 page_action)
+{
+ u16 pg_sz = sizeof(struct mpi3_driver_page2);
+ struct mpi3_driver_page2 *drvr_page2 = NULL;
+ u8 trigger_type, num_triggers;
+ int retval;
+ int i = 0;
+ unsigned long flags;
+
+ retval = mpi3mr_get_num_trigger(mrioc, &num_triggers, page_action);
+
+ if (retval)
+ goto out;
+
+ pg_sz = offsetof(struct mpi3_driver_page2, trigger) +
+ (num_triggers * sizeof(union mpi3_driver2_trigger_element));
+ drvr_page2 = kzalloc(pg_sz, GFP_KERNEL);
+ if (!drvr_page2) {
+ retval = -ENOMEM;
+ goto out;
+ }
+
+ retval = mpi3mr_cfg_get_driver_pg2(mrioc, drvr_page2, pg_sz, page_action);
+ if (retval) {
+ dprint_init(mrioc, "%s: driver page 2 read failed\n", __func__);
+ kfree(drvr_page2);
+ goto out;
+ }
+ spin_lock_irqsave(&mrioc->trigger_lock, flags);
+ kfree(mrioc->driver_pg2);
+ mrioc->driver_pg2 = drvr_page2;
+ mrioc->reply_trigger_present = false;
+ mrioc->event_trigger_present = false;
+ mrioc->scsisense_trigger_present = false;
+
+ for (i = 0; (i < mrioc->driver_pg2->num_triggers); i++) {
+ trigger_type = mrioc->driver_pg2->trigger[i].event.type;
+ switch (trigger_type) {
+ case MPI3_DRIVER2_TRIGGER_TYPE_REPLY:
+ mrioc->reply_trigger_present = true;
+ break;
+ case MPI3_DRIVER2_TRIGGER_TYPE_EVENT:
+ mrioc->event_trigger_present = true;
+ break;
+ case MPI3_DRIVER2_TRIGGER_TYPE_SCSI_SENSE:
+ mrioc->scsisense_trigger_present = true;
+ break;
+ default:
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
+out:
+ return retval;
+}
+
+/**
+ * mpi3mr_release_diag_bufs - Release diag buffers
+ * @mrioc: Adapter instance reference
+ * @skip_rel_action: Skip release action and set buffer state
+ *
+ * This function calls helper function to release both trace and
+ * firmware buffers from the controller.
+ *
+ * Return: None
+ */
+void mpi3mr_release_diag_bufs(struct mpi3mr_ioc *mrioc, u8 skip_rel_action)
+{
+ u8 i;
+ struct diag_buffer_desc *diag_buffer;
+
+ for (i = 0; i < MPI3MR_MAX_NUM_HDB; i++) {
+ diag_buffer = &mrioc->diag_buffers[i];
+ if (!(diag_buffer->addr))
+ continue;
+ if (diag_buffer->status == MPI3MR_HDB_BUFSTATUS_RELEASED)
+ continue;
+ if (!skip_rel_action)
+ mpi3mr_issue_diag_buf_release(mrioc, diag_buffer);
+ diag_buffer->status = MPI3MR_HDB_BUFSTATUS_RELEASED;
+ atomic64_inc(&event_counter);
+ }
+}
+
+/**
+ * mpi3mr_set_trigger_data_in_hdb - Updates HDB trigger type and
+ * trigger data
+ *
+ * @hdb: HDB pointer
+ * @type: Trigger type
+ * @data: Trigger data
+ * @force: Trigger overwrite flag
+ * @trigger_data: Pointer to trigger data information
+ *
+ * Updates trigger type and trigger data based on parameter
+ * passed to this function
+ *
+ * Return: Nothing
+ */
+void mpi3mr_set_trigger_data_in_hdb(struct diag_buffer_desc *hdb,
+ u8 type, union mpi3mr_trigger_data *trigger_data, bool force)
+{
+ if ((!force) && (hdb->trigger_type != MPI3MR_HDB_TRIGGER_TYPE_UNKNOWN))
+ return;
+ hdb->trigger_type = type;
+ if (!trigger_data)
+ memset(&hdb->trigger_data, 0, sizeof(*trigger_data));
+ else
+ memcpy(&hdb->trigger_data, trigger_data, sizeof(*trigger_data));
+}
+
+/**
+ * mpi3mr_set_trigger_data_in_all_hdb - Updates HDB trigger type
+ * and trigger data for all HDB
+ *
+ * @mrioc: Adapter instance reference
+ * @type: Trigger type
+ * @data: Trigger data
+ * @force: Trigger overwrite flag
+ * @trigger_data: Pointer to trigger data information
+ *
+ * Updates trigger type and trigger data based on parameter
+ * passed to this function
+ *
+ * Return: Nothing
+ */
+void mpi3mr_set_trigger_data_in_all_hdb(struct mpi3mr_ioc *mrioc,
+ u8 type, union mpi3mr_trigger_data *trigger_data, bool force)
+{
+ struct diag_buffer_desc *hdb = NULL;
+
+ hdb = mpi3mr_diag_buffer_for_type(mrioc, MPI3_DIAG_BUFFER_TYPE_TRACE);
+ if (hdb)
+ mpi3mr_set_trigger_data_in_hdb(hdb, type, trigger_data, force);
+ hdb = mpi3mr_diag_buffer_for_type(mrioc, MPI3_DIAG_BUFFER_TYPE_FW);
+ if (hdb)
+ mpi3mr_set_trigger_data_in_hdb(hdb, type, trigger_data, force);
+}
+
+/**
+ * mpi3mr_hdbstatuschg_evt_th - HDB status change evt tophalf
+ * @mrioc: Adapter instance reference
+ * @event_reply: event data
+ *
+ * Modifies the status of the applicable diag buffer descriptors
+ *
+ * Return: Nothing
+ */
+void mpi3mr_hdbstatuschg_evt_th(struct mpi3mr_ioc *mrioc,
+ struct mpi3_event_notification_reply *event_reply)
+{
+ struct mpi3_event_data_diag_buffer_status_change *evtdata;
+ struct diag_buffer_desc *diag_buffer;
+
+ evtdata = (struct mpi3_event_data_diag_buffer_status_change *)
+ event_reply->event_data;
+
+ diag_buffer = mpi3mr_diag_buffer_for_type(mrioc, evtdata->type);
+ if (!diag_buffer)
+ return;
+ if ((diag_buffer->status != MPI3MR_HDB_BUFSTATUS_POSTED_UNPAUSED) &&
+ (diag_buffer->status != MPI3MR_HDB_BUFSTATUS_POSTED_PAUSED))
+ return;
+ switch (evtdata->reason_code) {
+ case MPI3_EVENT_DIAG_BUFFER_STATUS_CHANGE_RC_RELEASED:
+ {
+ diag_buffer->status = MPI3MR_HDB_BUFSTATUS_RELEASED;
+ mpi3mr_set_trigger_data_in_hdb(diag_buffer,
+ MPI3MR_HDB_TRIGGER_TYPE_FW_RELEASED, NULL, 0);
+ atomic64_inc(&event_counter);
+ break;
+ }
+ case MPI3_EVENT_DIAG_BUFFER_STATUS_CHANGE_RC_RESUMED:
+ {
+ diag_buffer->status = MPI3MR_HDB_BUFSTATUS_POSTED_UNPAUSED;
+ break;
+ }
+ case MPI3_EVENT_DIAG_BUFFER_STATUS_CHANGE_RC_PAUSED:
+ {
+ diag_buffer->status = MPI3MR_HDB_BUFSTATUS_POSTED_PAUSED;
+ break;
+ }
+ default:
+ dprint_event_th(mrioc, "%s: unknown reason_code(%d)\n",
+ __func__, evtdata->reason_code);
+ break;
+ }
+}
+
+/**
+ * mpi3mr_diag_buffer_for_type - returns buffer desc for type
+ * @mrioc: Adapter instance reference
+ * @buf_type: Diagnostic buffer type
+ *
+ * Identifies matching diag descriptor from mrioc for given diag
+ * buffer type.
+ *
+ * Return: diag buffer descriptor on success, NULL on failures.
+ */
+
+struct diag_buffer_desc *
+mpi3mr_diag_buffer_for_type(struct mpi3mr_ioc *mrioc, u8 buf_type)
+{
+ u8 i;
+
+ for (i = 0; i < MPI3MR_MAX_NUM_HDB; i++) {
+ if (mrioc->diag_buffers[i].type == buf_type)
+ return &mrioc->diag_buffers[i];
+ }
+ return NULL;
+}
+
+/**
* mpi3mr_bsg_pel_abort - sends PEL abort request
* @mrioc: Adapter instance reference
*
@@ -126,6 +941,259 @@ static struct mpi3mr_ioc *mpi3mr_bsg_verify_adapter(int ioc_number)
}
/**
+ * mpi3mr_bsg_refresh_hdb_triggers - Refresh HDB trigger data
+ * @mrioc: Adapter instance reference
+ * @job: BSG Job pointer
+ *
+ * This function reads the controller trigger config page as
+ * defined by the input page type and refreshes the driver's
+ * local trigger information structures with the controller's
+ * config page data.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long
+mpi3mr_bsg_refresh_hdb_triggers(struct mpi3mr_ioc *mrioc,
+ struct bsg_job *job)
+{
+ struct mpi3mr_bsg_out_refresh_hdb_triggers refresh_triggers;
+ uint32_t data_out_sz;
+ u8 page_action;
+ long rval = -EINVAL;
+
+ data_out_sz = job->request_payload.payload_len;
+
+ if (data_out_sz != sizeof(refresh_triggers)) {
+ dprint_bsg_err(mrioc, "%s: invalid size argument\n",
+ __func__);
+ return rval;
+ }
+
+ if (mrioc->unrecoverable) {
+ dprint_bsg_err(mrioc, "%s: unrecoverable controller\n",
+ __func__);
+ return -EFAULT;
+ }
+ if (mrioc->reset_in_progress) {
+ dprint_bsg_err(mrioc, "%s: reset in progress\n", __func__);
+ return -EAGAIN;
+ }
+
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ &refresh_triggers, sizeof(refresh_triggers));
+
+ switch (refresh_triggers.page_type) {
+ case MPI3MR_HDB_REFRESH_TYPE_CURRENT:
+ page_action = MPI3_CONFIG_ACTION_READ_CURRENT;
+ break;
+ case MPI3MR_HDB_REFRESH_TYPE_DEFAULT:
+ page_action = MPI3_CONFIG_ACTION_READ_DEFAULT;
+ break;
+ case MPI3MR_HDB_HDB_REFRESH_TYPE_PERSISTENT:
+ page_action = MPI3_CONFIG_ACTION_READ_PERSISTENT;
+ break;
+ default:
+ dprint_bsg_err(mrioc,
+ "%s: unsupported refresh trigger, page_type %d\n",
+ __func__, refresh_triggers.page_type);
+ return rval;
+ }
+ rval = mpi3mr_refresh_trigger(mrioc, page_action);
+
+ return rval;
+}
+
+/**
+ * mpi3mr_bsg_upload_hdb - Upload a specific HDB to user space
+ * @mrioc: Adapter instance reference
+ * @job: BSG Job pointer
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_bsg_upload_hdb(struct mpi3mr_ioc *mrioc,
+ struct bsg_job *job)
+{
+ struct mpi3mr_bsg_out_upload_hdb upload_hdb;
+ struct diag_buffer_desc *diag_buffer;
+ uint32_t data_out_size;
+ uint32_t data_in_size;
+
+ data_out_size = job->request_payload.payload_len;
+ data_in_size = job->reply_payload.payload_len;
+
+ if (data_out_size != sizeof(upload_hdb)) {
+ dprint_bsg_err(mrioc, "%s: invalid size argument\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ &upload_hdb, sizeof(upload_hdb));
+
+ if ((!upload_hdb.length) || (data_in_size != upload_hdb.length)) {
+ dprint_bsg_err(mrioc, "%s: invalid length argument\n",
+ __func__);
+ return -EINVAL;
+ }
+ diag_buffer = mpi3mr_diag_buffer_for_type(mrioc, upload_hdb.buf_type);
+ if ((!diag_buffer) || (!diag_buffer->addr)) {
+ dprint_bsg_err(mrioc, "%s: invalid buffer type %d\n",
+ __func__, upload_hdb.buf_type);
+ return -EINVAL;
+ }
+
+ if ((diag_buffer->status != MPI3MR_HDB_BUFSTATUS_RELEASED) &&
+ (diag_buffer->status != MPI3MR_HDB_BUFSTATUS_POSTED_PAUSED)) {
+ dprint_bsg_err(mrioc,
+ "%s: invalid buffer status %d for type %d\n",
+ __func__, diag_buffer->status, upload_hdb.buf_type);
+ return -EINVAL;
+ }
+
+ if ((upload_hdb.start_offset + upload_hdb.length) > diag_buffer->size) {
+ dprint_bsg_err(mrioc,
+ "%s: invalid start offset %d, length %d for type %d\n",
+ __func__, upload_hdb.start_offset, upload_hdb.length,
+ upload_hdb.buf_type);
+ return -EINVAL;
+ }
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ (diag_buffer->addr + upload_hdb.start_offset),
+ data_in_size);
+ return 0;
+}
+
+/**
+ * mpi3mr_bsg_repost_hdb - Re-post HDB
+ * @mrioc: Adapter instance reference
+ * @job: BSG job pointer
+ *
+ * This function retrieves the HDB descriptor corresponding to a
+ * given buffer type and if the HDB is in released status then
+ * posts the HDB with the firmware.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_bsg_repost_hdb(struct mpi3mr_ioc *mrioc,
+ struct bsg_job *job)
+{
+ struct mpi3mr_bsg_out_repost_hdb repost_hdb;
+ struct diag_buffer_desc *diag_buffer;
+ uint32_t data_out_sz;
+
+ data_out_sz = job->request_payload.payload_len;
+
+ if (data_out_sz != sizeof(repost_hdb)) {
+ dprint_bsg_err(mrioc, "%s: invalid size argument\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (mrioc->unrecoverable) {
+ dprint_bsg_err(mrioc, "%s: unrecoverable controller\n",
+ __func__);
+ return -EFAULT;
+ }
+ if (mrioc->reset_in_progress) {
+ dprint_bsg_err(mrioc, "%s: reset in progress\n", __func__);
+ return -EAGAIN;
+ }
+
+ sg_copy_to_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ &repost_hdb, sizeof(repost_hdb));
+
+ diag_buffer = mpi3mr_diag_buffer_for_type(mrioc, repost_hdb.buf_type);
+ if ((!diag_buffer) || (!diag_buffer->addr)) {
+ dprint_bsg_err(mrioc, "%s: invalid buffer type %d\n",
+ __func__, repost_hdb.buf_type);
+ return -EINVAL;
+ }
+
+ if (diag_buffer->status != MPI3MR_HDB_BUFSTATUS_RELEASED) {
+ dprint_bsg_err(mrioc,
+ "%s: invalid buffer status %d for type %d\n",
+ __func__, diag_buffer->status, repost_hdb.buf_type);
+ return -EINVAL;
+ }
+
+ if (mpi3mr_issue_diag_buf_post(mrioc, diag_buffer)) {
+ dprint_bsg_err(mrioc, "%s: post failed for type %d\n",
+ __func__, repost_hdb.buf_type);
+ return -EFAULT;
+ }
+ mpi3mr_set_trigger_data_in_hdb(diag_buffer,
+ MPI3MR_HDB_TRIGGER_TYPE_UNKNOWN, NULL, 1);
+
+ return 0;
+}
+
+/**
+ * mpi3mr_bsg_query_hdb - Handler for query HDB command
+ * @mrioc: Adapter instance reference
+ * @job: BSG job pointer
+ *
+ * This function prepares and copies the host diagnostic buffer
+ * entries to the user buffer.
+ *
+ * Return: 0 on success and proper error codes on failure
+ */
+static long mpi3mr_bsg_query_hdb(struct mpi3mr_ioc *mrioc,
+ struct bsg_job *job)
+{
+ long rval = 0;
+ struct mpi3mr_bsg_in_hdb_status *hbd_status;
+ struct mpi3mr_hdb_entry *hbd_status_entry;
+ u32 length, min_length;
+ u8 i;
+ struct diag_buffer_desc *diag_buffer;
+ uint32_t data_in_sz = 0;
+
+ data_in_sz = job->request_payload.payload_len;
+
+ length = (sizeof(*hbd_status) + ((MPI3MR_MAX_NUM_HDB - 1) *
+ sizeof(*hbd_status_entry)));
+ hbd_status = kmalloc(length, GFP_KERNEL);
+ if (!hbd_status)
+ return -ENOMEM;
+ hbd_status_entry = &hbd_status->entry[0];
+
+ hbd_status->num_hdb_types = MPI3MR_MAX_NUM_HDB;
+ for (i = 0; i < MPI3MR_MAX_NUM_HDB; i++) {
+ diag_buffer = &mrioc->diag_buffers[i];
+ hbd_status_entry->buf_type = diag_buffer->type;
+ hbd_status_entry->status = diag_buffer->status;
+ hbd_status_entry->trigger_type = diag_buffer->trigger_type;
+ memcpy(&hbd_status_entry->trigger_data,
+ &diag_buffer->trigger_data,
+ sizeof(hbd_status_entry->trigger_data));
+ hbd_status_entry->size = (diag_buffer->size / 1024);
+ hbd_status_entry++;
+ }
+ hbd_status->element_trigger_format =
+ MPI3MR_HDB_QUERY_ELEMENT_TRIGGER_FORMAT_DATA;
+
+ if (data_in_sz < 4) {
+ dprint_bsg_err(mrioc, "%s: invalid size passed\n", __func__);
+ rval = -EINVAL;
+ goto out;
+ }
+ min_length = min(data_in_sz, length);
+ if (job->request_payload.payload_len >= min_length) {
+ sg_copy_from_buffer(job->request_payload.sg_list,
+ job->request_payload.sg_cnt,
+ hbd_status, min_length);
+ rval = 0;
+ }
+out:
+ kfree(hbd_status);
+ return rval;
+}
+
+
+/**
* mpi3mr_enable_logdata - Handler for log data enable
* @mrioc: Adapter instance reference
* @job: BSG job reference
@@ -553,6 +1621,18 @@ static long mpi3mr_bsg_process_drv_cmds(struct bsg_job *job)
case MPI3MR_DRVBSG_OPCODE_PELENABLE:
rval = mpi3mr_bsg_pel_enable(mrioc, job);
break;
+ case MPI3MR_DRVBSG_OPCODE_QUERY_HDB:
+ rval = mpi3mr_bsg_query_hdb(mrioc, job);
+ break;
+ case MPI3MR_DRVBSG_OPCODE_REPOST_HDB:
+ rval = mpi3mr_bsg_repost_hdb(mrioc, job);
+ break;
+ case MPI3MR_DRVBSG_OPCODE_UPLOAD_HDB:
+ rval = mpi3mr_bsg_upload_hdb(mrioc, job);
+ break;
+ case MPI3MR_DRVBSG_OPCODE_REFRESH_HDB_TRIGGERS:
+ rval = mpi3mr_bsg_refresh_hdb_triggers(mrioc, job);
+ break;
case MPI3MR_DRVBSG_OPCODE_UNKNOWN:
default:
pr_err("%s: unsupported driver command opcode %d\n",
diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
index c2a22e96f7b7..458c856dda4b 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_fw.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
@@ -274,6 +274,9 @@ static void mpi3mr_print_event_data(struct mpi3mr_ioc *mrioc,
case MPI3_EVENT_PREPARE_FOR_RESET:
desc = "Prepare For Reset";
break;
+ case MPI3_EVENT_DIAGNOSTIC_BUFFER_STATUS_CHANGE:
+ desc = "Diagnostic Buffer Status Change";
+ break;
}
if (!desc)
@@ -342,13 +345,14 @@ static void mpi3mr_process_admin_reply_desc(struct mpi3mr_ioc *mrioc,
{
u16 reply_desc_type, host_tag = 0;
u16 ioc_status = MPI3_IOCSTATUS_SUCCESS;
- u32 ioc_loginfo = 0;
+ u32 ioc_loginfo = 0, sense_count = 0;
struct mpi3_status_reply_descriptor *status_desc;
struct mpi3_address_reply_descriptor *addr_desc;
struct mpi3_success_reply_descriptor *success_desc;
struct mpi3_default_reply *def_reply = NULL;
struct mpi3mr_drv_cmd *cmdptr = NULL;
struct mpi3_scsi_io_reply *scsi_reply;
+ struct scsi_sense_hdr sshdr;
u8 *sense_buf = NULL;
*reply_dma = 0;
@@ -363,6 +367,7 @@ static void mpi3mr_process_admin_reply_desc(struct mpi3mr_ioc *mrioc,
MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL)
ioc_loginfo = le32_to_cpu(status_desc->ioc_log_info);
ioc_status &= MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK;
+ mpi3mr_reply_trigger(mrioc, ioc_status, ioc_loginfo);
break;
case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_ADDRESS_REPLY:
addr_desc = (struct mpi3_address_reply_descriptor *)reply_desc;
@@ -380,7 +385,15 @@ static void mpi3mr_process_admin_reply_desc(struct mpi3mr_ioc *mrioc,
scsi_reply = (struct mpi3_scsi_io_reply *)def_reply;
sense_buf = mpi3mr_get_sensebuf_virt_addr(mrioc,
le64_to_cpu(scsi_reply->sense_data_buffer_address));
+ sense_count = le32_to_cpu(scsi_reply->sense_count);
+ if (sense_buf) {
+ scsi_normalize_sense(sense_buf, sense_count,
+ &sshdr);
+ mpi3mr_scsisense_trigger(mrioc, sshdr.sense_key,
+ sshdr.asc, sshdr.ascq);
+ }
}
+ mpi3mr_reply_trigger(mrioc, ioc_status, ioc_loginfo);
break;
case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_SUCCESS:
success_desc = (struct mpi3_success_reply_descriptor *)reply_desc;
@@ -938,6 +951,14 @@ static const struct {
},
{ MPI3MR_RESET_FROM_SYSFS, "sysfs invocation" },
{ MPI3MR_RESET_FROM_SYSFS_TIMEOUT, "sysfs TM timeout" },
+ {
+ MPI3MR_RESET_FROM_DIAG_BUFFER_POST_TIMEOUT,
+ "diagnostic buffer post timeout"
+ },
+ {
+ MPI3MR_RESET_FROM_DIAG_BUFFER_RELEASE_TIMEOUT,
+ "diagnostic buffer release timeout"
+ },
{ MPI3MR_RESET_FROM_FIRMWARE, "firmware asynchronous reset" },
{ MPI3MR_RESET_FROM_CFG_REQ_TIMEOUT, "configuration request timeout"},
{ MPI3MR_RESET_FROM_SAS_TRANSPORT_TIMEOUT, "timeout of a SAS transport layer request" },
@@ -2387,6 +2408,7 @@ out:
void mpi3mr_check_rh_fault_ioc(struct mpi3mr_ioc *mrioc, u32 reason_code)
{
u32 ioc_status, host_diagnostic, timeout;
+ union mpi3mr_trigger_data trigger_data;
if (mrioc->unrecoverable) {
ioc_err(mrioc, "controller is unrecoverable\n");
@@ -2398,16 +2420,30 @@ void mpi3mr_check_rh_fault_ioc(struct mpi3mr_ioc *mrioc, u32 reason_code)
ioc_err(mrioc, "controller is not present\n");
return;
}
-
+ memset(&trigger_data, 0, sizeof(trigger_data));
ioc_status = readl(&mrioc->sysif_regs->ioc_status);
- if ((ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY) ||
- (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT)) {
+
+ if (ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY) {
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_FW_RELEASED, NULL, 0);
+ return;
+ } else if (ioc_status & MPI3_SYSIF_IOC_STATUS_FAULT) {
+ trigger_data.fault = (readl(&mrioc->sysif_regs->fault) &
+ MPI3_SYSIF_FAULT_CODE_MASK);
+
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_FAULT, &trigger_data, 0);
mpi3mr_print_fault_info(mrioc);
return;
}
+
mpi3mr_set_diagsave(mrioc);
mpi3mr_issue_reset(mrioc, MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT,
reason_code);
+ trigger_data.fault = (readl(&mrioc->sysif_regs->fault) &
+ MPI3_SYSIF_FAULT_CODE_MASK);
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc, MPI3MR_HDB_TRIGGER_TYPE_FAULT,
+ &trigger_data, 0);
timeout = MPI3_SYSIF_DIAG_SAVE_TIMEOUT * 10;
do {
host_diagnostic = readl(&mrioc->sysif_regs->host_diagnostic);
@@ -2587,7 +2623,8 @@ static void mpi3mr_watchdog_work(struct work_struct *work)
container_of(work, struct mpi3mr_ioc, watchdog_work.work);
unsigned long flags;
enum mpi3mr_iocstate ioc_state;
- u32 fault, host_diagnostic, ioc_status;
+ u32 host_diagnostic, ioc_status;
+ union mpi3mr_trigger_data trigger_data;
u16 reset_reason = MPI3MR_RESET_FROM_FAULT_WATCH;
if (mrioc->reset_in_progress)
@@ -2618,8 +2655,11 @@ static void mpi3mr_watchdog_work(struct work_struct *work)
return;
}
+ memset(&trigger_data, 0, sizeof(trigger_data));
ioc_status = readl(&mrioc->sysif_regs->ioc_status);
if (ioc_status & MPI3_SYSIF_IOC_STATUS_RESET_HISTORY) {
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_FW_RELEASED, NULL, 0);
mpi3mr_soft_reset_handler(mrioc, MPI3MR_RESET_FROM_FIRMWARE, 0);
return;
}
@@ -2629,7 +2669,9 @@ static void mpi3mr_watchdog_work(struct work_struct *work)
if (ioc_state != MRIOC_STATE_FAULT)
goto schedule_work;
- fault = readl(&mrioc->sysif_regs->fault) & MPI3_SYSIF_FAULT_CODE_MASK;
+ trigger_data.fault = readl(&mrioc->sysif_regs->fault) & MPI3_SYSIF_FAULT_CODE_MASK;
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_FAULT, &trigger_data, 0);
host_diagnostic = readl(&mrioc->sysif_regs->host_diagnostic);
if (host_diagnostic & MPI3_SYSIF_HOST_DIAG_SAVE_IN_PROGRESS) {
if (!mrioc->diagsave_timeout) {
@@ -2643,7 +2685,7 @@ static void mpi3mr_watchdog_work(struct work_struct *work)
mpi3mr_print_fault_info(mrioc);
mrioc->diagsave_timeout = 0;
- switch (fault) {
+ switch (trigger_data.fault) {
case MPI3_SYSIF_FAULT_CODE_COMPLETE_RESET_NEEDED:
case MPI3_SYSIF_FAULT_CODE_POWER_CYCLE_REQUIRED:
ioc_warn(mrioc,
@@ -3003,7 +3045,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 =
@@ -3682,6 +3728,94 @@ static const struct {
};
/**
+ * 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
*
@@ -3898,6 +4032,7 @@ static int mpi3mr_enable_events(struct mpi3mr_ioc *mrioc)
mpi3mr_unmask_events(mrioc, MPI3_EVENT_PREPARE_FOR_RESET);
mpi3mr_unmask_events(mrioc, MPI3_EVENT_CABLE_MGMT);
mpi3mr_unmask_events(mrioc, MPI3_EVENT_ENERGY_PACK_CHANGE);
+ mpi3mr_unmask_events(mrioc, MPI3_EVENT_DIAGNOSTIC_BUFFER_STATUS_CHANGE);
retval = mpi3mr_issue_event_notification(mrioc);
if (retval)
@@ -3989,9 +4124,18 @@ retry_init:
}
}
+ 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) {
@@ -4067,6 +4211,12 @@ retry_init:
goto out_failed;
}
+ retval = mpi3mr_refresh_trigger(mrioc, MPI3_CONFIG_ACTION_READ_CURRENT);
+ if (retval) {
+ ioc_err(mrioc, "failed to refresh triggers\n");
+ goto out_failed;
+ }
+
ioc_info(mrioc, "controller initialization completed successfully\n");
return retval;
out_failed:
@@ -4144,6 +4294,17 @@ retry_init:
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 +4570,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 +4705,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;
@@ -4980,6 +5155,7 @@ int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc,
int retval = 0, i;
unsigned long flags;
u32 host_diagnostic, timeout = MPI3_SYSIF_DIAG_SAVE_TIMEOUT * 10;
+ union mpi3mr_trigger_data trigger_data;
/* Block the reset handler until diag save in progress*/
dprint_reset(mrioc,
@@ -5012,10 +5188,16 @@ int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc,
mrioc->reset_in_progress = 1;
mrioc->stop_bsgs = 1;
mrioc->prev_reset_result = -1;
+ memset(&trigger_data, 0, sizeof(trigger_data));
if ((!snapdump) && (reset_reason != MPI3MR_RESET_FROM_FAULT_WATCH) &&
(reset_reason != MPI3MR_RESET_FROM_FIRMWARE) &&
(reset_reason != MPI3MR_RESET_FROM_CIACTIV_FAULT)) {
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_SOFT_RESET, NULL, 0);
+ 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;
@@ -5032,6 +5214,8 @@ int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc,
retval = mpi3mr_issue_reset(mrioc,
MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, reset_reason);
if (!retval) {
+ trigger_data.fault = (readl(&mrioc->sysif_regs->fault) &
+ MPI3_SYSIF_FAULT_CODE_MASK);
do {
host_diagnostic =
readl(&mrioc->sysif_regs->host_diagnostic);
@@ -5040,6 +5224,8 @@ int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc,
break;
msleep(100);
} while (--timeout);
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_FAULT, &trigger_data, 0);
}
}
@@ -5075,6 +5261,15 @@ 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);
+ mrioc->fw_release_trigger_active = false;
+ mrioc->trace_release_trigger_active = false;
+ mrioc->snapdump_trigger_active = false;
+ mpi3mr_set_trigger_data_in_all_hdb(mrioc,
+ MPI3MR_HDB_TRIGGER_TYPE_SOFT_RESET, NULL, 0);
+
+ dprint_reset(mrioc,
+ "soft_reset_handler: reinitializing the controller\n");
retval = mpi3mr_reinit_ioc(mrioc, 0);
if (retval) {
pr_err(IOCNAME "reinit after soft reset failed: reason %d\n",
@@ -5954,3 +6149,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;
+}
+
diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c
index bce639a6cca1..eac179dc9370 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_os.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_os.c
@@ -242,6 +242,40 @@ static void mpi3mr_fwevt_add_to_list(struct mpi3mr_ioc *mrioc,
}
/**
+ * mpi3mr_hdb_trigger_data_event - Add hdb trigger data event to
+ * the list
+ * @mrioc: Adapter instance reference
+ * @event_data: Event data
+ *
+ * Add the given hdb trigger data event to the firmware event
+ * list.
+ *
+ * Return: Nothing.
+ */
+void mpi3mr_hdb_trigger_data_event(struct mpi3mr_ioc *mrioc,
+ struct trigger_event_data *event_data)
+{
+ struct mpi3mr_fwevt *fwevt;
+ u16 sz = sizeof(*event_data);
+
+ fwevt = mpi3mr_alloc_fwevt(sz);
+ if (!fwevt) {
+ ioc_warn(mrioc, "failed to queue hdb trigger data event\n");
+ return;
+ }
+
+ fwevt->mrioc = mrioc;
+ fwevt->event_id = MPI3MR_DRIVER_EVENT_PROCESS_TRIGGER;
+ fwevt->send_ack = 0;
+ fwevt->process_evt = 1;
+ fwevt->evt_ctx = 0;
+ fwevt->event_data_size = sz;
+ memcpy(fwevt->event_data, event_data, sz);
+
+ mpi3mr_fwevt_add_to_list(mrioc, fwevt);
+}
+
+/**
* mpi3mr_fwevt_del_from_list - Delete firmware event from list
* @mrioc: Adapter instance reference
* @fwevt: Firmware event reference
@@ -898,6 +932,8 @@ void mpi3mr_remove_tgtdev_from_host(struct mpi3mr_ioc *mrioc,
}
} else
mpi3mr_remove_tgtdev_from_sas_transport(mrioc, tgtdev);
+ mpi3mr_global_trigger(mrioc,
+ MPI3_DRIVER2_GLOBALTRIGGER_DEVICE_REMOVAL_ENABLED);
ioc_info(mrioc, "%s :Removed handle(0x%04x), wwid(0x%016llx)\n",
__func__, tgtdev->dev_handle, (unsigned long long)tgtdev->wwid);
@@ -1434,6 +1470,62 @@ out:
}
/**
+ * mpi3mr_process_trigger_data_event_bh - Process trigger event
+ * data
+ * @mrioc: Adapter instance reference
+ * @event_data: Event data
+ *
+ * This function releases diage buffers or issues diag fault
+ * based on trigger conditions
+ *
+ * Return: Nothing
+ */
+static void mpi3mr_process_trigger_data_event_bh(struct mpi3mr_ioc *mrioc,
+ struct trigger_event_data *event_data)
+{
+ struct diag_buffer_desc *trace_hdb = event_data->trace_hdb;
+ struct diag_buffer_desc *fw_hdb = event_data->fw_hdb;
+ unsigned long flags;
+ int retval = 0;
+ u8 trigger_type = event_data->trigger_type;
+ union mpi3mr_trigger_data *trigger_data =
+ &event_data->trigger_specific_data;
+
+ if (event_data->snapdump) {
+ if (trace_hdb)
+ mpi3mr_set_trigger_data_in_hdb(trace_hdb, trigger_type,
+ trigger_data, 1);
+ if (fw_hdb)
+ mpi3mr_set_trigger_data_in_hdb(fw_hdb, trigger_type,
+ trigger_data, 1);
+ mpi3mr_soft_reset_handler(mrioc,
+ MPI3MR_RESET_FROM_TRIGGER, 1);
+ return;
+ }
+
+ if (trace_hdb) {
+ retval = mpi3mr_issue_diag_buf_release(mrioc, trace_hdb);
+ if (!retval) {
+ mpi3mr_set_trigger_data_in_hdb(trace_hdb, trigger_type,
+ trigger_data, 1);
+ }
+ spin_lock_irqsave(&mrioc->trigger_lock, flags);
+ mrioc->trace_release_trigger_active = false;
+ spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
+ }
+ if (fw_hdb) {
+ retval = mpi3mr_issue_diag_buf_release(mrioc, fw_hdb);
+ if (!retval) {
+ mpi3mr_set_trigger_data_in_hdb(fw_hdb, trigger_type,
+ trigger_data, 1);
+ }
+ spin_lock_irqsave(&mrioc->trigger_lock, flags);
+ mrioc->fw_release_trigger_active = false;
+ spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
+ }
+}
+
+/**
* mpi3mr_encldev_add_chg_evt_debug - debug for enclosure event
* @mrioc: Adapter instance reference
* @encl_pg0: Enclosure page 0.
@@ -2019,6 +2111,12 @@ static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc,
"scan for non responding and newly added devices after soft reset completed\n");
break;
}
+ case MPI3MR_DRIVER_EVENT_PROCESS_TRIGGER:
+ {
+ mpi3mr_process_trigger_data_event_bh(mrioc,
+ (struct trigger_event_data *)fwevt->event_data);
+ break;
+ }
default:
break;
}
@@ -2857,6 +2955,7 @@ void mpi3mr_os_handle_events(struct mpi3mr_ioc *mrioc,
ack_req = 1;
evt_type = event_reply->event;
+ mpi3mr_event_trigger(mrioc, event_reply->event);
switch (evt_type) {
case MPI3_EVENT_DEVICE_ADDED:
@@ -2895,6 +2994,11 @@ void mpi3mr_os_handle_events(struct mpi3mr_ioc *mrioc,
ack_req = 0;
break;
}
+ case MPI3_EVENT_DIAGNOSTIC_BUFFER_STATUS_CHANGE:
+ {
+ mpi3mr_hdbstatuschg_evt_th(mrioc, event_reply);
+ break;
+ }
case MPI3_EVENT_DEVICE_INFO_CHANGED:
case MPI3_EVENT_LOG_DATA:
case MPI3_EVENT_ENCL_DEVICE_STATUS_CHANGE:
@@ -3158,6 +3262,7 @@ void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc,
MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL)
ioc_loginfo = le32_to_cpu(status_desc->ioc_log_info);
ioc_status &= MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK;
+ mpi3mr_reply_trigger(mrioc, ioc_status, ioc_loginfo);
break;
case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_ADDRESS_REPLY:
addr_desc = (struct mpi3_address_reply_descriptor *)reply_desc;
@@ -3186,6 +3291,12 @@ void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc,
ioc_status &= MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK;
if (sense_state == MPI3_SCSI_STATE_SENSE_BUFF_Q_EMPTY)
panic("%s: Ran out of sense buffers\n", mrioc->name);
+ if (sense_buf) {
+ scsi_normalize_sense(sense_buf, sense_count, &sshdr);
+ mpi3mr_scsisense_trigger(mrioc, sshdr.sense_key,
+ sshdr.asc, sshdr.ascq);
+ }
+ mpi3mr_reply_trigger(mrioc, ioc_status, ioc_loginfo);
break;
case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_SUCCESS:
success_desc = (struct mpi3_success_reply_descriptor *)reply_desc;
@@ -3811,6 +3922,8 @@ int mpi3mr_issue_tm(struct mpi3mr_ioc *mrioc, u8 tm_type,
default:
break;
}
+ mpi3mr_global_trigger(mrioc,
+ MPI3_DRIVER2_GLOBALTRIGGER_TASK_MANAGEMENT_ENABLED);
out_unlock:
drv_cmd->state = MPI3MR_CMD_NOTUSED;
diff --git a/include/uapi/scsi/scsi_bsg_mpi3mr.h b/include/uapi/scsi/scsi_bsg_mpi3mr.h
index a3ba779a3f78..f5ea1db92339 100644
--- a/include/uapi/scsi/scsi_bsg_mpi3mr.h
+++ b/include/uapi/scsi/scsi_bsg_mpi3mr.h
@@ -296,6 +296,7 @@ struct mpi3mr_hdb_entry {
* multiple hdb entries.
*
* @num_hdb_types: Number of host diag buffer types supported
+ * @element_trigger_format: Element trigger format
* @rsvd1: Reserved
* @rsvd2: Reserved
* @rsvd3: Reserved
@@ -303,7 +304,7 @@ struct mpi3mr_hdb_entry {
*/
struct mpi3mr_bsg_in_hdb_status {
__u8 num_hdb_types;
- __u8 rsvd1;
+ __u8 element_trigger_format;
__u16 rsvd2;
__u32 rsvd3;
struct mpi3mr_hdb_entry entry[1];