scsi: mpi3mr: Add ioctl support for HDB

Add interface for applications to manage the host diagnostic buffers and
update the automatic diag buffer capture triggers.

Co-developed-by: Sathya Prakash <sathya.prakash@broadcom.com>
Signed-off-by: Sathya Prakash <sathya.prakash@broadcom.com>
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
Link: https://lore.kernel.org/r/20240626102646.14298-4-ranjan.kumar@broadcom.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Ranjan Kumar 2024-06-26 15:56:45 +05:30 committed by Martin K. Petersen
parent d8d08d1638
commit 78b506984e
3 changed files with 279 additions and 1 deletions

View File

@ -200,6 +200,18 @@ extern atomic64_t event_counter;
#define MPI3MR_HDB_TRIGGER_TYPE_SOFT_RESET 4 #define MPI3MR_HDB_TRIGGER_TYPE_SOFT_RESET 4
#define MPI3MR_HDB_TRIGGER_TYPE_FW_RELEASED 5 #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 */ /* SGE Flag definition */
#define MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST \ #define MPI3MR_SGEFLAGS_SYSTEM_SIMPLE_END_OF_LIST \
(MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE | MPI3_SGE_FLAGS_DLAS_SYSTEM | \ (MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE | MPI3_SGE_FLAGS_DLAS_SYSTEM | \

View File

@ -940,6 +940,259 @@ static struct mpi3mr_ioc *mpi3mr_bsg_verify_adapter(int ioc_number)
return NULL; return NULL;
} }
/**
* 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 * mpi3mr_enable_logdata - Handler for log data enable
* @mrioc: Adapter instance reference * @mrioc: Adapter instance reference
@ -1368,6 +1621,18 @@ static long mpi3mr_bsg_process_drv_cmds(struct bsg_job *job)
case MPI3MR_DRVBSG_OPCODE_PELENABLE: case MPI3MR_DRVBSG_OPCODE_PELENABLE:
rval = mpi3mr_bsg_pel_enable(mrioc, job); rval = mpi3mr_bsg_pel_enable(mrioc, job);
break; 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: case MPI3MR_DRVBSG_OPCODE_UNKNOWN:
default: default:
pr_err("%s: unsupported driver command opcode %d\n", pr_err("%s: unsupported driver command opcode %d\n",

View File

@ -296,6 +296,7 @@ struct mpi3mr_hdb_entry {
* multiple hdb entries. * multiple hdb entries.
* *
* @num_hdb_types: Number of host diag buffer types supported * @num_hdb_types: Number of host diag buffer types supported
* @element_trigger_format: Element trigger format
* @rsvd1: Reserved * @rsvd1: Reserved
* @rsvd2: Reserved * @rsvd2: Reserved
* @rsvd3: Reserved * @rsvd3: Reserved
@ -303,7 +304,7 @@ struct mpi3mr_hdb_entry {
*/ */
struct mpi3mr_bsg_in_hdb_status { struct mpi3mr_bsg_in_hdb_status {
__u8 num_hdb_types; __u8 num_hdb_types;
__u8 rsvd1; __u8 element_trigger_format;
__u16 rsvd2; __u16 rsvd2;
__u32 rsvd3; __u32 rsvd3;
struct mpi3mr_hdb_entry entry[1]; struct mpi3mr_hdb_entry entry[1];