]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
scsi: mpi3mr: Add ioctl support for HDB
authorRanjan Kumar <ranjan.kumar@broadcom.com>
Wed, 26 Jun 2024 10:26:45 +0000 (15:56 +0530)
committerMartin K. Petersen <martin.petersen@oracle.com>
Thu, 27 Jun 2024 03:30:09 +0000 (23:30 -0400)
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>
drivers/scsi/mpi3mr/mpi3mr.h
drivers/scsi/mpi3mr/mpi3mr_app.c
include/uapi/scsi/scsi_bsg_mpi3mr.h

index e14982a785a6a6a1e8c98b5c392276115cc7f643..1c604ed24c6e9ac2183992afda51afead63a55b6 100644 (file)
@@ -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 | \
index 85c69ba105d8203a3ae3cd9920d0d98ceb9d64c0..f73f265c7921a48d224b42e9f663f2e705519821 100644 (file)
@@ -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",
index a3ba779a3f7877021c8cda7d99d739e4fcfd91e1..f5ea1db92339400590659879ac6109c836a4449a 100644 (file)
@@ -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];