Commit 78b50698 authored by Ranjan Kumar's avatar Ranjan Kumar Committed by Martin K. Petersen
Browse files

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: default avatarSathya Prakash <sathya.prakash@broadcom.com>
Signed-off-by: default avatarSathya Prakash <sathya.prakash@broadcom.com>
Signed-off-by: default avatarRanjan Kumar <ranjan.kumar@broadcom.com>
Link: https://lore.kernel.org/r/20240626102646.14298-4-ranjan.kumar@broadcom.com


Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent d8d08d16
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -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 | \
+265 −0
Original line number Diff line number Diff line
@@ -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",
+2 −1
Original line number Diff line number Diff line
@@ -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];