tb_seg_alloc_failed: if (mrioc->trace_buf_pool) { for (i = 0; i < mrioc->num_tb_segs; i++) { if (mrioc->trace_buf[i].segment) {
dma_pool_free(mrioc->trace_buf_pool,
mrioc->trace_buf[i].segment,
mrioc->trace_buf[i].segment_dma);
mrioc->trace_buf[i].segment = NULL;
}
mrioc->trace_buf[i].segment = NULL;
}
dma_pool_destroy(mrioc->trace_buf_pool);
mrioc->trace_buf_pool = NULL;
}
trace_buf_pool_failed:
kfree(mrioc->trace_buf);
mrioc->trace_buf = NULL;
trace_buf_failed: if (diag_buffer_list)
dma_free_coherent(&mrioc->pdev->dev, sizeof(u64) * mrioc->num_tb_segs,
diag_buffer_list, diag_buffer_list_dma); return -1;
}
/** * mpi3mr_alloc_diag_bufs - Allocate memory for diag buffers * @mrioc: Adapter instance reference * * This functions checks whether the driver defined buffer sizes * are greater than IOCFacts provided controller local buffer * sizes and if the driver defined sizes are more then the * driver allocates the specific buffer by reading driver page1 * * Return: Nothing.
*/ void mpi3mr_alloc_diag_bufs(struct mpi3mr_ioc *mrioc)
{ struct diag_buffer_desc *diag_buffer; struct mpi3_driver_page1 driver_pg1;
u32 trace_dec_size, trace_min_size, fw_dec_size, fw_min_size,
trace_size, fw_size;
u16 pg_sz = sizeof(driver_pg1); int retval = 0; bool retry = false;
if (mrioc->diag_buffers[0].addr || mrioc->diag_buffers[1].addr) return;
/** * mpi3mr_issue_diag_buf_post - Send diag buffer post req * @mrioc: Adapter instance reference * @diag_buffer: Diagnostic buffer descriptor * * Issue diagnostic buffer post MPI request through admin queue * and wait for the completion of it or time out. * * Return: 0 on success, non-zero on failures.
*/ int mpi3mr_issue_diag_buf_post(struct mpi3mr_ioc *mrioc, struct diag_buffer_desc *diag_buffer)
{ struct mpi3_diag_buffer_post_request diag_buf_post_req;
u8 prev_status; int retval = 0;
if (diag_buffer->disabled_after_reset) {
dprint_bsg_err(mrioc, "%s: skipping diag buffer posting\n" "as it is disabled after reset\n", __func__); return -1;
}
/** * mpi3mr_post_diag_bufs - Post diag buffers to the controller * @mrioc: Adapter instance reference * * This function calls helper function to post both trace and * firmware buffers to the controller. * * Return: None
*/ int mpi3mr_post_diag_bufs(struct mpi3mr_ioc *mrioc)
{
u8 i; struct diag_buffer_desc *diag_buffer;
for (i = 0; i < MPI3MR_MAX_NUM_HDB; i++) {
diag_buffer = &mrioc->diag_buffers[i]; if (!(diag_buffer->addr)) continue; if (mpi3mr_issue_diag_buf_post(mrioc, diag_buffer)) return -1;
} return 0;
}
/** * mpi3mr_issue_diag_buf_release - Send diag buffer release req * @mrioc: Adapter instance reference * @diag_buffer: Diagnostic buffer descriptor * * Issue diagnostic buffer manage MPI request with release * action request through admin queue and wait for the * completion of it or time out. * * Return: 0 on success, non-zero on failures.
*/ int mpi3mr_issue_diag_buf_release(struct mpi3mr_ioc *mrioc, struct diag_buffer_desc *diag_buffer)
{ struct mpi3_diag_buffer_manage_request diag_buf_manage_req; int retval = 0;
if ((diag_buffer->status != MPI3MR_HDB_BUFSTATUS_POSTED_UNPAUSED) &&
(diag_buffer->status != MPI3MR_HDB_BUFSTATUS_POSTED_PAUSED)) return retval;
/** * mpi3mr_process_trigger - Generic HDB Trigger handler * @mrioc: Adapter instance reference * @trigger_type: Trigger type * @trigger_data: Trigger data * @trigger_flags: Trigger flags * * This function checks validity of HDB, triggers and based on * trigger information, creates an event to be processed in the * firmware event worker thread . * * This function should be called with trigger spinlock held * * Return: Nothing
*/ staticvoid mpi3mr_process_trigger(struct mpi3mr_ioc *mrioc, u8 trigger_type, union mpi3mr_trigger_data *trigger_data, u8 trigger_flags)
{ struct trigger_event_data event_data; struct diag_buffer_desc *trace_hdb = NULL; struct diag_buffer_desc *fw_hdb = NULL;
u64 global_trigger;
if (event_data.trace_hdb || event_data.fw_hdb)
mpi3mr_hdb_trigger_data_event(mrioc, &event_data);
}
/** * mpi3mr_global_trigger - Global HDB trigger handler * @mrioc: Adapter instance reference * @trigger_data: Trigger data * * This function checks whether the given global trigger is * enabled in the driver page 2 and if so calls generic trigger * handler to queue event for HDB release. * * Return: Nothing
*/ void mpi3mr_global_trigger(struct mpi3mr_ioc *mrioc, u64 trigger_data)
{ unsignedlong flags; union mpi3mr_trigger_data trigger_specific_data;
for (i = 0; i < num_triggers; i++, event_trigger++) { if (event_trigger->type !=
MPI3_DRIVER2_TRIGGER_TYPE_EVENT) continue; if (event_trigger->event != event) continue;
trigger_flags = event_trigger->flags;
mpi3mr_process_trigger(mrioc,
MPI3MR_HDB_TRIGGER_TYPE_ELEMENT,
(union mpi3mr_trigger_data *)event_trigger,
trigger_flags); break;
}
spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
}
}
/** * mpi3mr_reply_trigger - MPI Reply HDB trigger handler * @mrioc: Adapter instance reference * @ioc_status: Masked value of IOC Status from MPI Reply * @ioc_loginfo: IOC Log Info from MPI Reply * * This function compares IOC status and IOC log info trigger * values with driver page 2 values and calls generic trigger * handler to release HDBs if match found. * * Return: Nothing
*/ void mpi3mr_reply_trigger(struct mpi3mr_ioc *mrioc, u16 ioc_status,
u32 ioc_loginfo)
{ struct mpi3_driver2_trigger_reply *reply_trigger = NULL;
u64 i = 0; unsignedlong flags;
u8 num_triggers, trigger_flags;
if (mrioc->reply_trigger_present) {
spin_lock_irqsave(&mrioc->trigger_lock, flags);
reply_trigger = (struct mpi3_driver2_trigger_reply *)
mrioc->driver_pg2->trigger;
num_triggers = mrioc->driver_pg2->num_triggers; for (i = 0; i < num_triggers; i++, reply_trigger++) { if (reply_trigger->type !=
MPI3_DRIVER2_TRIGGER_TYPE_REPLY) continue; if ((le16_to_cpu(reply_trigger->ioc_status) !=
ioc_status)
&& (le16_to_cpu(reply_trigger->ioc_status) !=
MPI3_DRIVER2_TRIGGER_REPLY_IOCSTATUS_MATCH_ALL)) continue; if ((le32_to_cpu(reply_trigger->ioc_log_info) !=
(le32_to_cpu(reply_trigger->ioc_log_info_mask) &
ioc_loginfo))) continue;
trigger_flags = reply_trigger->flags;
mpi3mr_process_trigger(mrioc,
MPI3MR_HDB_TRIGGER_TYPE_ELEMENT,
(union mpi3mr_trigger_data *)reply_trigger,
trigger_flags); break;
}
spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
}
}
/** * mpi3mr_get_num_trigger - Gets number of HDB triggers * @mrioc: Adapter instance reference * @num_triggers: Number of triggers * @page_action: Page action * * This function reads number of triggers by reading driver page * 2 * * Return: 0 on success and proper error codes on failure
*/ staticint mpi3mr_get_num_trigger(struct mpi3mr_ioc *mrioc, u8 *num_triggers,
u8 page_action)
{ struct mpi3_driver_page2 drvr_page2; int retval = 0;
/** * mpi3mr_refresh_trigger - Handler for Refresh trigger BSG * @mrioc: Adapter instance reference * @page_action: Page action * * This function caches the driver page 2 in the driver's memory * by reading driver page 2 from the controller for a given page * type and updates the HDB trigger values * * Return: 0 on success and proper error codes on failure
*/ int mpi3mr_refresh_trigger(struct mpi3mr_ioc *mrioc, u8 page_action)
{
u16 pg_sz = sizeof(struct mpi3_driver_page2); struct mpi3_driver_page2 *drvr_page2 = NULL;
u8 trigger_type, num_triggers; int retval; int i = 0; unsignedlong flags;
for (i = 0; (i < mrioc->driver_pg2->num_triggers); i++) {
trigger_type = mrioc->driver_pg2->trigger[i].event.type; switch (trigger_type) { case MPI3_DRIVER2_TRIGGER_TYPE_REPLY:
mrioc->reply_trigger_present = true; break; case MPI3_DRIVER2_TRIGGER_TYPE_EVENT:
mrioc->event_trigger_present = true; break; case MPI3_DRIVER2_TRIGGER_TYPE_SCSI_SENSE:
mrioc->scsisense_trigger_present = true; break; default: break;
}
}
spin_unlock_irqrestore(&mrioc->trigger_lock, flags);
out: return retval;
}
/** * mpi3mr_release_diag_bufs - Release diag buffers * @mrioc: Adapter instance reference * @skip_rel_action: Skip release action and set buffer state * * This function calls helper function to release both trace and * firmware buffers from the controller. * * Return: None
*/ void mpi3mr_release_diag_bufs(struct mpi3mr_ioc *mrioc, u8 skip_rel_action)
{
u8 i; struct diag_buffer_desc *diag_buffer;
for (i = 0; i < MPI3MR_MAX_NUM_HDB; i++) {
diag_buffer = &mrioc->diag_buffers[i]; if (!(diag_buffer->addr)) continue; if (diag_buffer->status == MPI3MR_HDB_BUFSTATUS_RELEASED) continue; if (!skip_rel_action)
mpi3mr_issue_diag_buf_release(mrioc, diag_buffer);
diag_buffer->status = MPI3MR_HDB_BUFSTATUS_RELEASED;
atomic64_inc(&event_counter);
}
}
/** * mpi3mr_set_trigger_data_in_hdb - Updates HDB trigger type and * trigger data * * @hdb: HDB pointer * @type: Trigger type * @trigger_data: Pointer to trigger data information * @force: Trigger overwrite flag * * Updates trigger type and trigger data based on parameter * passed to this function * * Return: Nothing
*/ void mpi3mr_set_trigger_data_in_hdb(struct diag_buffer_desc *hdb,
u8 type, union mpi3mr_trigger_data *trigger_data, bool force)
{ if ((!force) && (hdb->trigger_type != MPI3MR_HDB_TRIGGER_TYPE_UNKNOWN)) return;
hdb->trigger_type = type; if (!trigger_data)
memset(&hdb->trigger_data, 0, sizeof(*trigger_data)); else
memcpy(&hdb->trigger_data, trigger_data, sizeof(*trigger_data));
}
/** * mpi3mr_set_trigger_data_in_all_hdb - Updates HDB trigger type * and trigger data for all HDB * * @mrioc: Adapter instance reference * @type: Trigger type * @trigger_data: Pointer to trigger data information * @force: Trigger overwrite flag * * Updates trigger type and trigger data based on parameter * passed to this function * * Return: Nothing
*/ void mpi3mr_set_trigger_data_in_all_hdb(struct mpi3mr_ioc *mrioc,
u8 type, union mpi3mr_trigger_data *trigger_data, bool force)
{ struct diag_buffer_desc *hdb = NULL;
out_unlock:
mrioc->pel_abort_cmd.state = MPI3MR_CMD_NOTUSED;
mutex_unlock(&mrioc->pel_abort_cmd.mutex); return retval;
} /** * mpi3mr_bsg_verify_adapter - verify adapter number is valid * @ioc_number: Adapter number * * This function returns the adapter instance pointer of given * adapter number. If adapter number does not match with the * driver's adapter list, driver returns NULL. * * Return: adapter instance reference
*/ staticstruct mpi3mr_ioc *mpi3mr_bsg_verify_adapter(int ioc_number)
{ struct mpi3mr_ioc *mrioc = 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
*/ staticlong
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;
/** * 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
*/ staticlong 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;
}
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
*/ staticlong 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;
/** * mpi3mr_enable_logdata - Handler for log data enable * @mrioc: Adapter instance reference * @job: BSG job reference * * This function enables log data caching in the driver if not * already enabled and return the maximum number of log data * entries that can be cached in the driver. * * Return: 0 on success and proper error codes on failure
*/ staticlong mpi3mr_enable_logdata(struct mpi3mr_ioc *mrioc, struct bsg_job *job)
{ struct mpi3mr_logdata_enable logdata_enable;
return -EINVAL;
} /** * mpi3mr_get_logdata - Handler for get log data * @mrioc: Adapter instance reference * @job: BSG job pointer * This function copies the log data entries to the user buffer * when log caching is enabled in the driver. * * Return: 0 on success and proper error codes on failure
*/ staticlong mpi3mr_get_logdata(struct mpi3mr_ioc *mrioc, struct bsg_job *job)
{
u16 num_entries, sz, entry_sz = mrioc->logdata_entry_sz;
if ((!mrioc->logdata_buf) || (job->request_payload.payload_len < entry_sz)) return -EINVAL;
/** * mpi3mr_bsg_pel_enable - Handler for PEL enable driver * @mrioc: Adapter instance reference * @job: BSG job pointer * * This function is the handler for PEL enable driver. * Validates the application given class and locale and if * requires aborts the existing PEL wait request and/or issues * new PEL wait request to the firmware and returns. * * Return: 0 on success and proper error codes on failure.
*/ staticlong mpi3mr_bsg_pel_enable(struct mpi3mr_ioc *mrioc, struct bsg_job *job)
{ long rval = -EINVAL; struct mpi3mr_bsg_out_pel_enable pel_enable;
u8 issue_pel_wait;
u8 tmp_class;
u16 tmp_locale;
out: return rval;
} /** * mpi3mr_get_all_tgt_info - Get all target information * @mrioc: Adapter instance reference * @job: BSG job reference * * This function copies the driver managed target devices device * handle, persistent ID, bus ID and taret ID to the user * provided buffer for the specific controller. This function * also provides the number of devices managed by the driver for * the specific controller. * * Return: 0 on success and proper error codes on failure
*/ staticlong mpi3mr_get_all_tgt_info(struct mpi3mr_ioc *mrioc, struct bsg_job *job)
{
u16 num_devices = 0, i = 0, size; unsignedlong flags; struct mpi3mr_tgt_dev *tgtdev; struct mpi3mr_device_map_info *devmap_info = NULL; struct mpi3mr_all_tgt_info *alltgt_info = NULL;
uint32_t min_entrylen = 0, kern_entrylen = 0, usr_entrylen = 0;
sg_copy_from_buffer(job->request_payload.sg_list,
job->request_payload.sg_cnt,
alltgt_info, (min_entrylen + sizeof(u64)));
kfree(alltgt_info); return 0;
} /** * mpi3mr_get_change_count - Get topology change count * @mrioc: Adapter instance reference * @job: BSG job reference * * This function copies the toplogy change count provided by the * driver in events and cached in the driver to the user * provided buffer for the specific controller. * * Return: 0 on success and proper error codes on failure
*/ staticlong mpi3mr_get_change_count(struct mpi3mr_ioc *mrioc, struct bsg_job *job)
{ struct mpi3mr_change_count chgcnt;
/** * mpi3mr_bsg_adp_reset - Issue controller reset * @mrioc: Adapter instance reference * @job: BSG job reference * * This function identifies the user provided reset type and * issues approporiate reset to the controller and wait for that * to complete and reinitialize the controller and then returns * * Return: 0 on success and proper error codes on failure
*/ staticlong mpi3mr_bsg_adp_reset(struct mpi3mr_ioc *mrioc, struct bsg_job *job)
{ long rval = -EINVAL;
u8 save_snapdump; struct mpi3mr_bsg_adp_reset adpreset;
/** * mpi3mr_bsg_process_drv_cmds - Driver Command handler * @job: BSG job reference * * This function is the top level handler for driver commands, * this does basic validation of the buffer and identifies the * opcode and switches to correct sub handler. * * Return: 0 on success and proper error codes on failure
*/ staticlong mpi3mr_bsg_process_drv_cmds(struct bsg_job *job)
{ long rval = -EINVAL; struct mpi3mr_ioc *mrioc = NULL; struct mpi3mr_bsg_packet *bsg_req = NULL; struct mpi3mr_bsg_drv_cmd *drvrcmd = NULL;
if (mutex_lock_interruptible(&mrioc->bsg_cmds.mutex)) return -ERESTARTSYS;
switch (drvrcmd->opcode) { case MPI3MR_DRVBSG_OPCODE_ADPRESET:
rval = mpi3mr_bsg_adp_reset(mrioc, job); break; case MPI3MR_DRVBSG_OPCODE_ALLTGTDEVINFO:
rval = mpi3mr_get_all_tgt_info(mrioc, job); break; case MPI3MR_DRVBSG_OPCODE_GETCHGCNT:
rval = mpi3mr_get_change_count(mrioc, job); break; case MPI3MR_DRVBSG_OPCODE_LOGDATAENABLE:
rval = mpi3mr_enable_logdata(mrioc, job); break; case MPI3MR_DRVBSG_OPCODE_GETLOGDATA:
rval = mpi3mr_get_logdata(mrioc, job); break; 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",
MPI3MR_DRIVER_NAME, drvrcmd->opcode); break;
}
mutex_unlock(&mrioc->bsg_cmds.mutex); return rval;
}
/** * mpi3mr_total_num_ioctl_sges - Count number of SGEs required * @drv_bufs: DMA address of the buffers to be placed in sgl * @bufcnt: Number of DMA buffers * * This function returns total number of data SGEs required * including zero length SGEs and excluding management request * and response buffer for the given list of data buffer * descriptors * * Return: Number of SGE elements needed
*/ staticinline u16 mpi3mr_total_num_ioctl_sges(struct mpi3mr_buf_map *drv_bufs,
u8 bufcnt)
{
u16 i, sge_count = 0;
for (i = 0; i < bufcnt; i++, drv_bufs++) { if (drv_bufs->data_dir == DMA_NONE ||
drv_bufs->kern_buf) continue;
sge_count += drv_bufs->num_dma_desc; if (!drv_bufs->num_dma_desc)
sge_count++;
} return sge_count;
}
/** * mpi3mr_bsg_build_sgl - SGL construction for MPI commands * @mrioc: Adapter instance reference * @mpi_req: MPI request * @sgl_offset: offset to start sgl in the MPI request * @drv_bufs: DMA address of the buffers to be placed in sgl * @bufcnt: Number of DMA buffers * @is_rmc: Does the buffer list has management command buffer * @is_rmr: Does the buffer list has management response buffer * @num_datasges: Number of data buffers in the list * * This function places the DMA address of the given buffers in * proper format as SGEs in the given MPI request. * * Return: 0 on success,-1 on failure
*/ staticint mpi3mr_bsg_build_sgl(struct mpi3mr_ioc *mrioc, u8 *mpi_req,
u32 sgl_offset, struct mpi3mr_buf_map *drv_bufs,
u8 bufcnt, u8 is_rmc, u8 is_rmr, u8 num_datasges)
{ struct mpi3_request_header *mpi_header =
(struct mpi3_request_header *)mpi_req;
u8 *sgl = (mpi_req + sgl_offset), count = 0; struct mpi3_mgmt_passthrough_request *rmgmt_req =
(struct mpi3_mgmt_passthrough_request *)mpi_req; struct mpi3mr_buf_map *drv_buf_iter = drv_bufs;
u8 flag, sgl_flags, sgl_flag_eob, sgl_flags_last, last_chain_sgl_flag;
u16 available_sges, i, sges_needed;
u32 sge_element_size = sizeof(struct mpi3_sge_common); bool chain_used = false;
/** * mpi3mr_get_nvme_data_fmt - returns the NVMe data format * @nvme_encap_request: NVMe encapsulated MPI request * * This function returns the type of the data format specified * in user provided NVMe command in NVMe encapsulated request. * * Return: Data format of the NVMe command (PRP/SGL etc)
*/ staticunsignedint mpi3mr_get_nvme_data_fmt( struct mpi3_nvme_encapsulated_request *nvme_encap_request)
{
u8 format = 0;
format = ((nvme_encap_request->command[0] & 0xc000) >> 14); return format;
}
/** * mpi3mr_build_nvme_sgl - SGL constructor for NVME * encapsulated request * @mrioc: Adapter instance reference * @nvme_encap_request: NVMe encapsulated MPI request * @drv_bufs: DMA address of the buffers to be placed in sgl * @bufcnt: Number of DMA buffers * * This function places the DMA address of the given buffers in * proper format as SGEs in the given NVMe encapsulated request. * * Return: 0 on success, -1 on failure
*/ staticint mpi3mr_build_nvme_sgl(struct mpi3mr_ioc *mrioc, struct mpi3_nvme_encapsulated_request *nvme_encap_request, struct mpi3mr_buf_map *drv_bufs, u8 bufcnt)
{ struct mpi3mr_nvme_pt_sge *nvme_sgl;
__le64 sgl_dma;
u8 count;
size_t length = 0;
u16 available_sges = 0, i;
u32 sge_element_size = sizeof(struct mpi3mr_nvme_pt_sge); struct mpi3mr_buf_map *drv_buf_iter = drv_bufs;
u64 sgemod_mask = ((u64)((mrioc->facts.sge_mod_mask) <<
mrioc->facts.sge_mod_shift) << 32);
u64 sgemod_val = ((u64)(mrioc->facts.sge_mod_value) <<
mrioc->facts.sge_mod_shift) << 32;
u32 size;
/* * Not all commands require a data transfer. If no data, just return * without constructing any sgl.
*/ for (count = 0; count < bufcnt; count++, drv_buf_iter++) { if (drv_buf_iter->data_dir == DMA_NONE) continue;
length = drv_buf_iter->kern_buf_len; break;
} if (!length || !drv_buf_iter->num_dma_desc) return 0;
if (dev_pgsz > MPI3MR_IOCTL_SGE_SIZE) {
dprint_bsg_err(mrioc, "%s: NVMe device page size(%d) is greater than ioctl data sge size(%d) for handle 0x%04x\n",
__func__, dev_pgsz, MPI3MR_IOCTL_SGE_SIZE, dev_handle); return -1;
}
if (MPI3MR_IOCTL_SGE_SIZE % dev_pgsz) {
dprint_bsg_err(mrioc, "%s: ioctl data sge size(%d) is not a multiple of NVMe device page size(%d) for handle 0x%04x\n",
__func__, MPI3MR_IOCTL_SGE_SIZE, dev_pgsz, dev_handle); return -1;
}
/* * Not all commands require a data transfer. If no data, just return * without constructing any PRP.
*/ for (count = 0; count < bufcnt; count++, drv_buf_iter++) { if (drv_buf_iter->data_dir == DMA_NONE) continue;
length = drv_buf_iter->kern_buf_len; break;
}
if (!length || !drv_buf_iter->num_dma_desc) return 0;
for (count = 0; count < drv_buf_iter->num_dma_desc; count++) {
dma_addr = drv_buf_iter->dma_desc[count].dma_addr; if (dma_addr & page_mask) {
dprint_bsg_err(mrioc, "%s:dma_addr %pad is not aligned with page size 0x%x\n",
__func__, &dma_addr, dev_pgsz); return -1;
}
}
if (!mrioc->prp_list_virt) return -1;
mrioc->prp_sz = dev_pgsz;
/* * Set pointers to PRP1 and PRP2, which are in the NVMe command. * PRP1 is located at a 24 byte offset from the start of the NVMe * command. Then set the current PRP entry pointer to PRP1.
*/
prp1_entry = (__le64 *)((u8 *)(nvme_encap_request->command) +
MPI3MR_NVME_CMD_PRP1_OFFSET);
prp2_entry = (__le64 *)((u8 *)(nvme_encap_request->command) +
MPI3MR_NVME_CMD_PRP2_OFFSET);
prp_entry = prp1_entry; /* * For the PRP entries, use the specially allocated buffer of * contiguous memory.
*/
prp_page = (__le64 *)mrioc->prp_list_virt;
prp_page_dma = mrioc->prp_list_dma;
/* * Check if we are within 1 entry of a page boundary we don't * want our first entry to be a PRP List entry.
*/
page_mask_result = (uintptr_t)((u8 *)prp_page + prp_size) & page_mask; if (!page_mask_result) {
dprint_bsg_err(mrioc, "%s: PRP page is not page aligned\n",
__func__); goto err_out;
}
/* * Set PRP physical pointer, which initially points to the current PRP * DMA memory page.
*/
prp_entry_dma = prp_page_dma;
/* Loop while the length is not zero. */ while (length) {
page_mask_result = (prp_entry_dma + prp_size) & page_mask; if (!page_mask_result && (length > dev_pgsz)) {
dprint_bsg_err(mrioc, "%s: single PRP page is not sufficient\n",
__func__); goto err_out;
}
/* Need to handle if entry will be part of a page. */
offset = dma_addr & page_mask;
entry_len = dev_pgsz - offset;
if (prp_entry == prp1_entry) { /* * Must fill in the first PRP pointer (PRP1) before * moving on.
*/
*prp1_entry = cpu_to_le64(dma_addr); if (*prp1_entry & sgemod_mask) {
dprint_bsg_err(mrioc, "%s: PRP1 address collides with SGE modifier\n",
__func__); goto err_out;
}
*prp1_entry &= ~sgemod_mask;
*prp1_entry |= sgemod_val;
/* * Now point to the second PRP entry within the * command (PRP2).
*/
prp_entry = prp2_entry;
} elseif (prp_entry == prp2_entry) { /* * Should the PRP2 entry be a PRP List pointer or just * a regular PRP pointer? If there is more than one * more page of data, must use a PRP List pointer.
*/ if (length > dev_pgsz) { /* * PRP2 will contain a PRP List pointer because * more PRP's are needed with this command. The * list will start at the beginning of the * contiguous buffer.
*/
*prp2_entry = cpu_to_le64(prp_entry_dma); if (*prp2_entry & sgemod_mask) {
dprint_bsg_err(mrioc, "%s: PRP list address collides with SGE modifier\n",
__func__); goto err_out;
}
*prp2_entry &= ~sgemod_mask;
*prp2_entry |= sgemod_val;
/* * The next PRP Entry will be the start of the * first PRP List.
*/
prp_entry = prp_page; continue;
} else { /* * After this, the PRP Entries are complete. * This command uses 2 PRP's and no PRP list.
*/
*prp2_entry = cpu_to_le64(dma_addr); if (*prp2_entry & sgemod_mask) {
dprint_bsg_err(mrioc, "%s: PRP2 collides with SGE modifier\n",
__func__); goto err_out;
}
*prp2_entry &= ~sgemod_mask;
*prp2_entry |= sgemod_val;
}
} else { /* * Put entry in list and bump the addresses. * * After PRP1 and PRP2 are filled in, this will fill in * all remaining PRP entries in a PRP List, one per * each time through the loop.
*/
*prp_entry = cpu_to_le64(dma_addr); if (*prp_entry & sgemod_mask) {
dprint_bsg_err(mrioc, "%s: PRP address collides with SGE modifier\n",
__func__); goto err_out;
}
*prp_entry &= ~sgemod_mask;
*prp_entry |= sgemod_val;
prp_entry++;
prp_entry_dma += prp_size;
}
/* decrement length accounting for last partial page. */ if (entry_len >= length) {
length = 0;
} else { if (entry_len <= desc_len) {
dma_addr += entry_len;
desc_len -= entry_len;
} if (!desc_len) { if ((++desc_count) >=
drv_buf_iter->num_dma_desc) {
dprint_bsg_err(mrioc, "%s: Invalid len %zd while building PRP\n",
__func__, length); goto err_out;
}
dma_addr =
drv_buf_iter->dma_desc[desc_count].dma_addr;
desc_len =
drv_buf_iter->dma_desc[desc_count].size;
}
length -= entry_len;
}
}
/** * mpi3mr_map_data_buffer_dma - build dma descriptors for data * buffers * @mrioc: Adapter instance reference * @drv_buf: buffer map descriptor * @desc_count: Number of already consumed dma descriptors * * This function computes how many pre-allocated DMA descriptors * are required for the given data buffer and if those number of * descriptors are free, then setup the mapping of the scattered * DMA address to the given data buffer, if the data direction * of the buffer is DMA_TO_DEVICE then the actual data is copied to * the DMA buffers * * Return: 0 on success, -1 on failure
*/ staticint mpi3mr_map_data_buffer_dma(struct mpi3mr_ioc *mrioc, struct mpi3mr_buf_map *drv_buf,
u16 desc_count)
{
u16 i, needed_desc = drv_buf->kern_buf_len / MPI3MR_IOCTL_SGE_SIZE;
u32 buf_len = drv_buf->kern_buf_len, copied_len = 0;
if (drv_buf->kern_buf_len % MPI3MR_IOCTL_SGE_SIZE)
needed_desc++; if ((needed_desc + desc_count) > MPI3MR_NUM_IOCTL_SGE) {
dprint_bsg_err(mrioc, "%s: DMA descriptor mapping error %d:%d:%d\n",
__func__, needed_desc, desc_count, MPI3MR_NUM_IOCTL_SGE); return -1;
}
drv_buf->dma_desc = kzalloc(sizeof(*drv_buf->dma_desc) * needed_desc,
GFP_KERNEL); if (!drv_buf->dma_desc) return -1; for (i = 0; i < needed_desc; i++, desc_count++) {
drv_buf->dma_desc[i].addr = mrioc->ioctl_sge[desc_count].addr;
drv_buf->dma_desc[i].dma_addr =
mrioc->ioctl_sge[desc_count].dma_addr; if (buf_len < mrioc->ioctl_sge[desc_count].size)
drv_buf->dma_desc[i].size = buf_len; else
drv_buf->dma_desc[i].size =
mrioc->ioctl_sge[desc_count].size;
buf_len -= drv_buf->dma_desc[i].size;
memset(drv_buf->dma_desc[i].addr, 0,
mrioc->ioctl_sge[desc_count].size); if (drv_buf->data_dir == DMA_TO_DEVICE) {
memcpy(drv_buf->dma_desc[i].addr,
drv_buf->bsg_buf + copied_len,
drv_buf->dma_desc[i].size);
copied_len += drv_buf->dma_desc[i].size;
}
}
drv_buf->num_dma_desc = needed_desc; return 0;
} /** * mpi3mr_bsg_process_mpt_cmds - MPI Pass through BSG handler * @job: BSG job reference * * This function is the top level handler for MPI Pass through * command, this does basic validation of the input data buffers, * identifies the given buffer types and MPI command, allocates * DMAable memory for user given buffers, construstcs SGL * properly and passes the command to the firmware. * * Once the MPI command is completed the driver copies the data * if any and reply, sense information to user provided buffers. * If the command is timed out then issues controller reset * prior to returning. * * Return: 0 on success and proper error codes on failure
*/
mrioc = mpi3mr_bsg_verify_adapter(karg->mrioc_id); if (!mrioc) return -ENODEV;
if (mutex_lock_interruptible(&mrioc->bsg_cmds.mutex)) return -ERESTARTSYS;
if (mrioc->bsg_cmds.state & MPI3MR_CMD_PENDING) {
dprint_bsg_err(mrioc, "%s: command is in use\n", __func__);
mutex_unlock(&mrioc->bsg_cmds.mutex); return -EAGAIN;
}
if (!mrioc->ioctl_sges_allocated) {
mutex_unlock(&mrioc->bsg_cmds.mutex);
dprint_bsg_err(mrioc, "%s: DMA memory was not allocated\n",
__func__); return -ENOMEM;
}
if (karg->timeout < MPI3MR_APP_DEFAULT_TIMEOUT)
karg->timeout = MPI3MR_APP_DEFAULT_TIMEOUT;
/** * mpi3mr_app_save_logdata - Save Log Data events * @mrioc: Adapter instance reference * @event_data: event data associated with log data event * @event_data_size: event data size to copy * * If log data event caching is enabled by the applicatiobns, * then this function saves the log data in the circular queue * and Sends async signal SIGIO to indicate there is an async * event from the firmware to the event monitoring applications. * * Return:Nothing
*/ void mpi3mr_app_save_logdata(struct mpi3mr_ioc *mrioc, char *event_data,
u16 event_data_size)
{
u32 index = mrioc->logdata_buf_idx, sz; struct mpi3mr_logdata_entry *entry;
/** * mpi3mr_bsg_exit - de-registration from bsg layer * @mrioc: Adapter instance reference * * This will be called during driver unload and all * bsg resources allocated during load will be freed. * * Return:Nothing
*/ void mpi3mr_bsg_exit(struct mpi3mr_ioc *mrioc)
{ struct device *bsg_dev = &mrioc->bsg_dev; if (!mrioc->bsg_queue) return;
/** * reply_qfull_count_show - Show reply qfull count * @dev: class device * @attr: Device attributes * @buf: Buffer to copy * * Retrieves the current value of the reply_qfull_count from the mrioc structure and * formats it as a string for display. * * Return: sysfs_emit() return
*/ static ssize_t
reply_qfull_count_show(struct device *dev, struct device_attribute *attr, char *buf)
{ struct Scsi_Host *shost = class_to_shost(dev); struct mpi3mr_ioc *mrioc = shost_priv(shost);
/** * logging_level_show - Show controller debug level * @dev: class device * @attr: Device attributes * @buf: Buffer to copy * * A sysfs 'read/write' shost attribute, to show the current * debug log level used by the driver for the specific * controller. * * Return: sysfs_emit() return
*/ static ssize_t
logging_level_show(struct device *dev, struct device_attribute *attr, char *buf)
/** * sas_ncq_prio_supported_show - Indicate if device supports NCQ priority * @dev: pointer to embedded device * @attr: sas_ncq_prio_supported attribute descriptor * @buf: the buffer returned * * A sysfs 'read-only' sdev attribute, only works with SATA devices * * Returns: the number of characters written to @buf
*/ static ssize_t
sas_ncq_prio_supported_show(struct device *dev, struct device_attribute *attr, char *buf)
{ struct scsi_device *sdev = to_scsi_device(dev);
/** * sas_ncq_prio_enable_show - send prioritized io commands to device * @dev: pointer to embedded device * @attr: sas_ncq_prio_enable attribute descriptor * @buf: the buffer returned * * A sysfs 'read/write' sdev attribute, only works with SATA devices * * Returns: the number of characters written to @buf
*/ static ssize_t
sas_ncq_prio_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
{ struct scsi_device *sdev = to_scsi_device(dev); struct mpi3mr_sdev_priv_data *sdev_priv_data = sdev->hostdata;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.