@@ -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 | \
@@ -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
@@ -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",
@@ -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];