mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
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:
parent
d8d08d1638
commit
78b506984e
@ -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 | \
|
||||||
|
@ -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",
|
||||||
|
@ -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];
|
||||||
|
Loading…
Reference in New Issue
Block a user