Skip to content

Commit

Permalink
scsi: mpi3mr: Add ioctl support for HDB
Browse files Browse the repository at this point in the history
Add interface for applications to manage the host diagnostic buffers and
update the automatic diag buffer capture triggers.

Co-developed-by: Sathya Prakash <[email protected]>
Signed-off-by: Sathya Prakash <[email protected]>
Signed-off-by: Ranjan Kumar <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Martin K. Petersen <[email protected]>
  • Loading branch information
Ranjan Kumar authored and martinkpetersen committed Jun 27, 2024
1 parent d8d08d1 commit 78b5069
Show file tree
Hide file tree
Showing 3 changed files with 279 additions and 1 deletion.
12 changes: 12 additions & 0 deletions drivers/scsi/mpi3mr/mpi3mr.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,18 @@ extern atomic64_t event_counter;
#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 | \
Expand Down
265 changes: 265 additions & 0 deletions drivers/scsi/mpi3mr/mpi3mr_app.c
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,259 @@ static struct mpi3mr_ioc *mpi3mr_bsg_verify_adapter(int ioc_number)
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
* @mrioc: Adapter instance reference
Expand Down Expand Up @@ -1368,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",
Expand Down
3 changes: 2 additions & 1 deletion include/uapi/scsi/scsi_bsg_mpi3mr.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,14 +296,15 @@ 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
* @entry: Variable length Diag buffer status entry array
*/
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];
Expand Down

0 comments on commit 78b5069

Please sign in to comment.