forked from mirrors/linux
		
	scsi: mpi3mr: Add support for queue command processing
Send Port Enable Request to FW for Device Discovery. As part of port enable completion driver calls scan_start and scan_finished hooks. SCSI layer references like sdev, starget, etc. are added but actual device discovery will be supported once driver adds complete event process handling. Link: https://lore.kernel.org/r/20210520152545.2710479-5-kashyap.desai@broadcom.com Cc: sathya.prakash@broadcom.com Cc: hare@suse.de Reviewed-by: Tomas Henzl <thenzl@redhat.com> Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com> Reviewed-by: Hannes Reinecke <hare@suse.de> Signed-off-by: Kashyap Desai <kashyap.desai@broadcom.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
		
							parent
							
								
									c9566231cf
								
							
						
					
					
						commit
						023ab2a9b4
					
				
					 3 changed files with 927 additions and 2 deletions
				
			
		|  | @ -99,6 +99,7 @@ extern struct list_head mrioc_list; | ||||||
| 
 | 
 | ||||||
| /* command/controller interaction timeout definitions in seconds */ | /* command/controller interaction timeout definitions in seconds */ | ||||||
| #define MPI3MR_INTADMCMD_TIMEOUT		10 | #define MPI3MR_INTADMCMD_TIMEOUT		10 | ||||||
|  | #define MPI3MR_PORTENABLE_TIMEOUT		300 | ||||||
| #define MPI3MR_RESETTM_TIMEOUT			30 | #define MPI3MR_RESETTM_TIMEOUT			30 | ||||||
| #define MPI3MR_DEFAULT_SHUTDOWN_TIME		120 | #define MPI3MR_DEFAULT_SHUTDOWN_TIME		120 | ||||||
| 
 | 
 | ||||||
|  | @ -315,7 +316,43 @@ struct mpi3mr_intr_info { | ||||||
| 	char name[MPI3MR_NAME_LENGTH]; | 	char name[MPI3MR_NAME_LENGTH]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct mpi3mr_stgt_priv_data - SCSI target private structure | ||||||
|  |  * | ||||||
|  |  * @starget: Scsi_target pointer | ||||||
|  |  * @dev_handle: FW device handle | ||||||
|  |  * @perst_id: FW assigned Persistent ID | ||||||
|  |  * @num_luns: Number of Logical Units | ||||||
|  |  * @block_io: I/O blocked to the device or not | ||||||
|  |  * @dev_removed: Device removed in the Firmware | ||||||
|  |  * @dev_removedelay: Device is waiting to be removed in FW | ||||||
|  |  * @dev_type: Device type | ||||||
|  |  * @tgt_dev: Internal target device pointer | ||||||
|  |  */ | ||||||
|  | struct mpi3mr_stgt_priv_data { | ||||||
|  | 	struct scsi_target *starget; | ||||||
|  | 	u16 dev_handle; | ||||||
|  | 	u16 perst_id; | ||||||
|  | 	u32 num_luns; | ||||||
|  | 	atomic_t block_io; | ||||||
|  | 	u8 dev_removed; | ||||||
|  | 	u8 dev_removedelay; | ||||||
|  | 	u8 dev_type; | ||||||
|  | 	struct mpi3mr_tgt_dev *tgt_dev; | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct mpi3mr_stgt_priv_data - SCSI device private structure | ||||||
|  |  * | ||||||
|  |  * @tgt_priv_data: Scsi_target private data pointer | ||||||
|  |  * @lun_id: LUN ID of the device | ||||||
|  |  * @ncq_prio_enable: NCQ priority enable for SATA device | ||||||
|  |  */ | ||||||
|  | struct mpi3mr_sdev_priv_data { | ||||||
|  | 	struct mpi3mr_stgt_priv_data *tgt_priv_data; | ||||||
|  | 	u32 lun_id; | ||||||
|  | 	u8 ncq_prio_enable; | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * struct mpi3mr_drv_cmd - Internal command tracker |  * struct mpi3mr_drv_cmd - Internal command tracker | ||||||
|  | @ -445,12 +482,16 @@ struct scmd_priv { | ||||||
|  * @sbq_lock: Sense buffer queue lock |  * @sbq_lock: Sense buffer queue lock | ||||||
|  * @sbq_host_index: Sense buffer queuehost index |  * @sbq_host_index: Sense buffer queuehost index | ||||||
|  * @is_driver_loading: Is driver still loading |  * @is_driver_loading: Is driver still loading | ||||||
|  |  * @scan_started: Async scan started | ||||||
|  |  * @scan_failed: Asycn scan failed | ||||||
|  |  * @stop_drv_processing: Stop all command processing | ||||||
|  * @max_host_ios: Maximum host I/O count |  * @max_host_ios: Maximum host I/O count | ||||||
|  * @chain_buf_count: Chain buffer count |  * @chain_buf_count: Chain buffer count | ||||||
|  * @chain_buf_pool: Chain buffer pool |  * @chain_buf_pool: Chain buffer pool | ||||||
|  * @chain_sgl_list: Chain SGL list |  * @chain_sgl_list: Chain SGL list | ||||||
|  * @chain_bitmap_sz: Chain buffer allocator bitmap size |  * @chain_bitmap_sz: Chain buffer allocator bitmap size | ||||||
|  * @chain_bitmap: Chain buffer allocator bitmap |  * @chain_bitmap: Chain buffer allocator bitmap | ||||||
|  |  * @chain_buf_lock: Chain buffer list lock | ||||||
|  * @reset_in_progress: Reset in progress flag |  * @reset_in_progress: Reset in progress flag | ||||||
|  * @unrecoverable: Controller unrecoverable flag |  * @unrecoverable: Controller unrecoverable flag | ||||||
|  * @logging_level: Controller debug logging level |  * @logging_level: Controller debug logging level | ||||||
|  | @ -535,6 +576,9 @@ struct mpi3mr_ioc { | ||||||
| 	u32 sbq_host_index; | 	u32 sbq_host_index; | ||||||
| 
 | 
 | ||||||
| 	u8 is_driver_loading; | 	u8 is_driver_loading; | ||||||
|  | 	u8 scan_started; | ||||||
|  | 	u16 scan_failed; | ||||||
|  | 	u8 stop_drv_processing; | ||||||
| 
 | 
 | ||||||
| 	u16 max_host_ios; | 	u16 max_host_ios; | ||||||
| 
 | 
 | ||||||
|  | @ -543,6 +587,7 @@ struct mpi3mr_ioc { | ||||||
| 	struct chain_element *chain_sgl_list; | 	struct chain_element *chain_sgl_list; | ||||||
| 	u16  chain_bitmap_sz; | 	u16  chain_bitmap_sz; | ||||||
| 	void *chain_bitmap; | 	void *chain_bitmap; | ||||||
|  | 	spinlock_t chain_buf_lock; | ||||||
| 
 | 
 | ||||||
| 	u8 reset_in_progress; | 	u8 reset_in_progress; | ||||||
| 	u8 unrecoverable; | 	u8 unrecoverable; | ||||||
|  | @ -559,8 +604,11 @@ int mpi3mr_setup_resources(struct mpi3mr_ioc *mrioc); | ||||||
| void mpi3mr_cleanup_resources(struct mpi3mr_ioc *mrioc); | void mpi3mr_cleanup_resources(struct mpi3mr_ioc *mrioc); | ||||||
| int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc); | int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc); | ||||||
| void mpi3mr_cleanup_ioc(struct mpi3mr_ioc *mrioc); | void mpi3mr_cleanup_ioc(struct mpi3mr_ioc *mrioc); | ||||||
|  | int mpi3mr_issue_port_enable(struct mpi3mr_ioc *mrioc, u8 async); | ||||||
| int mpi3mr_admin_request_post(struct mpi3mr_ioc *mrioc, void *admin_req, | int mpi3mr_admin_request_post(struct mpi3mr_ioc *mrioc, void *admin_req, | ||||||
| u16 admin_req_sz, u8 ignore_reset); | u16 admin_req_sz, u8 ignore_reset); | ||||||
|  | int mpi3mr_op_request_post(struct mpi3mr_ioc *mrioc, | ||||||
|  | 			   struct op_req_qinfo *opreqq, u8 *req); | ||||||
| void mpi3mr_add_sg_single(void *paddr, u8 flags, u32 length, | void mpi3mr_add_sg_single(void *paddr, u8 flags, u32 length, | ||||||
| 			  dma_addr_t dma_addr); | 			  dma_addr_t dma_addr); | ||||||
| void mpi3mr_build_zero_len_sge(void *paddr); | void mpi3mr_build_zero_len_sge(void *paddr); | ||||||
|  | @ -571,6 +619,9 @@ void *mpi3mr_get_reply_virt_addr(struct mpi3mr_ioc *mrioc, | ||||||
| void mpi3mr_repost_sense_buf(struct mpi3mr_ioc *mrioc, | void mpi3mr_repost_sense_buf(struct mpi3mr_ioc *mrioc, | ||||||
| 				     u64 sense_buf_dma); | 				     u64 sense_buf_dma); | ||||||
| 
 | 
 | ||||||
|  | void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc, | ||||||
|  | 				  struct mpi3_default_reply_descriptor *reply_desc, | ||||||
|  | 				  u64 *reply_dma, u16 qidx); | ||||||
| void mpi3mr_start_watchdog(struct mpi3mr_ioc *mrioc); | void mpi3mr_start_watchdog(struct mpi3mr_ioc *mrioc); | ||||||
| void mpi3mr_stop_watchdog(struct mpi3mr_ioc *mrioc); | void mpi3mr_stop_watchdog(struct mpi3mr_ioc *mrioc); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -25,6 +25,22 @@ static inline void mpi3mr_writeq(__u64 b, volatile void __iomem *addr) | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | static inline bool | ||||||
|  | mpi3mr_check_req_qfull(struct op_req_qinfo *op_req_q) | ||||||
|  | { | ||||||
|  | 	u16 pi, ci, max_entries; | ||||||
|  | 	bool is_qfull = false; | ||||||
|  | 
 | ||||||
|  | 	pi = op_req_q->pi; | ||||||
|  | 	ci = READ_ONCE(op_req_q->ci); | ||||||
|  | 	max_entries = op_req_q->num_requests; | ||||||
|  | 
 | ||||||
|  | 	if ((ci == (pi + 1)) || ((!ci) && (pi == (max_entries - 1)))) | ||||||
|  | 		is_qfull = true; | ||||||
|  | 
 | ||||||
|  | 	return is_qfull; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void mpi3mr_sync_irqs(struct mpi3mr_ioc *mrioc) | static void mpi3mr_sync_irqs(struct mpi3mr_ioc *mrioc) | ||||||
| { | { | ||||||
| 	u16 i, max_vectors; | 	u16 i, max_vectors; | ||||||
|  | @ -283,6 +299,83 @@ static int mpi3mr_process_admin_reply_q(struct mpi3mr_ioc *mrioc) | ||||||
| 	return num_admin_replies; | 	return num_admin_replies; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * mpi3mr_get_reply_desc - get reply descriptor frame corresponding to | ||||||
|  |  *	queue's consumer index from operational reply descriptor queue. | ||||||
|  |  * @op_reply_q: op_reply_qinfo object | ||||||
|  |  * @reply_ci: operational reply descriptor's queue consumer index | ||||||
|  |  * | ||||||
|  |  * Returns reply descriptor frame address | ||||||
|  |  */ | ||||||
|  | static inline struct mpi3_default_reply_descriptor * | ||||||
|  | mpi3mr_get_reply_desc(struct op_reply_qinfo *op_reply_q, u32 reply_ci) | ||||||
|  | { | ||||||
|  | 	void *segment_base_addr; | ||||||
|  | 	struct segments *segments = op_reply_q->q_segments; | ||||||
|  | 	struct mpi3_default_reply_descriptor *reply_desc = NULL; | ||||||
|  | 
 | ||||||
|  | 	segment_base_addr = | ||||||
|  | 	    segments[reply_ci / op_reply_q->segment_qd].segment; | ||||||
|  | 	reply_desc = (struct mpi3_default_reply_descriptor *)segment_base_addr + | ||||||
|  | 	    (reply_ci % op_reply_q->segment_qd); | ||||||
|  | 	return reply_desc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mpi3mr_process_op_reply_q(struct mpi3mr_ioc *mrioc, | ||||||
|  | 	struct mpi3mr_intr_info *intr_info) | ||||||
|  | { | ||||||
|  | 	struct op_reply_qinfo *op_reply_q = intr_info->op_reply_q; | ||||||
|  | 	struct op_req_qinfo *op_req_q; | ||||||
|  | 	u32 exp_phase; | ||||||
|  | 	u32 reply_ci; | ||||||
|  | 	u32 num_op_reply = 0; | ||||||
|  | 	u64 reply_dma = 0; | ||||||
|  | 	struct mpi3_default_reply_descriptor *reply_desc; | ||||||
|  | 	u16 req_q_idx = 0, reply_qidx; | ||||||
|  | 
 | ||||||
|  | 	reply_qidx = op_reply_q->qid - 1; | ||||||
|  | 
 | ||||||
|  | 	exp_phase = op_reply_q->ephase; | ||||||
|  | 	reply_ci = op_reply_q->ci; | ||||||
|  | 
 | ||||||
|  | 	reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci); | ||||||
|  | 	if ((le16_to_cpu(reply_desc->reply_flags) & | ||||||
|  | 	    MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) { | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	do { | ||||||
|  | 		req_q_idx = le16_to_cpu(reply_desc->request_queue_id) - 1; | ||||||
|  | 		op_req_q = &mrioc->req_qinfo[req_q_idx]; | ||||||
|  | 
 | ||||||
|  | 		WRITE_ONCE(op_req_q->ci, le16_to_cpu(reply_desc->request_queue_ci)); | ||||||
|  | 		mpi3mr_process_op_reply_desc(mrioc, reply_desc, &reply_dma, | ||||||
|  | 		    reply_qidx); | ||||||
|  | 		if (reply_dma) | ||||||
|  | 			mpi3mr_repost_reply_buf(mrioc, reply_dma); | ||||||
|  | 		num_op_reply++; | ||||||
|  | 
 | ||||||
|  | 		if (++reply_ci == op_reply_q->num_replies) { | ||||||
|  | 			reply_ci = 0; | ||||||
|  | 			exp_phase ^= 1; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		reply_desc = mpi3mr_get_reply_desc(op_reply_q, reply_ci); | ||||||
|  | 
 | ||||||
|  | 		if ((le16_to_cpu(reply_desc->reply_flags) & | ||||||
|  | 		    MPI3_REPLY_DESCRIPT_FLAGS_PHASE_MASK) != exp_phase) | ||||||
|  | 			break; | ||||||
|  | 
 | ||||||
|  | 	} while (1); | ||||||
|  | 
 | ||||||
|  | 	writel(reply_ci, | ||||||
|  | 	    &mrioc->sysif_regs->oper_queue_indexes[reply_qidx].consumer_index); | ||||||
|  | 	op_reply_q->ci = reply_ci; | ||||||
|  | 	op_reply_q->ephase = exp_phase; | ||||||
|  | 
 | ||||||
|  | 	return num_op_reply; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static irqreturn_t mpi3mr_isr_primary(int irq, void *privdata) | static irqreturn_t mpi3mr_isr_primary(int irq, void *privdata) | ||||||
| { | { | ||||||
| 	struct mpi3mr_intr_info *intr_info = privdata; | 	struct mpi3mr_intr_info *intr_info = privdata; | ||||||
|  | @ -1302,6 +1395,74 @@ static int mpi3mr_create_op_queues(struct mpi3mr_ioc *mrioc) | ||||||
| 	return retval; | 	return retval; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * mpi3mr_op_request_post - Post request to operational queue | ||||||
|  |  * @mrioc: Adapter reference | ||||||
|  |  * @op_req_q: Operational request queue info | ||||||
|  |  * @req: MPI3 request | ||||||
|  |  * | ||||||
|  |  * Post the MPI3 request into operational request queue and | ||||||
|  |  * inform the controller, if the queue is full return | ||||||
|  |  * appropriate error. | ||||||
|  |  * | ||||||
|  |  * Return: 0 on success, non-zero on failure. | ||||||
|  |  */ | ||||||
|  | int mpi3mr_op_request_post(struct mpi3mr_ioc *mrioc, | ||||||
|  | 	struct op_req_qinfo *op_req_q, u8 *req) | ||||||
|  | { | ||||||
|  | 	u16 pi = 0, max_entries, reply_qidx = 0, midx; | ||||||
|  | 	int retval = 0; | ||||||
|  | 	unsigned long flags; | ||||||
|  | 	u8 *req_entry; | ||||||
|  | 	void *segment_base_addr; | ||||||
|  | 	u16 req_sz = mrioc->facts.op_req_sz; | ||||||
|  | 	struct segments *segments = op_req_q->q_segments; | ||||||
|  | 
 | ||||||
|  | 	reply_qidx = op_req_q->reply_qid - 1; | ||||||
|  | 
 | ||||||
|  | 	if (mrioc->unrecoverable) | ||||||
|  | 		return -EFAULT; | ||||||
|  | 
 | ||||||
|  | 	spin_lock_irqsave(&op_req_q->q_lock, flags); | ||||||
|  | 	pi = op_req_q->pi; | ||||||
|  | 	max_entries = op_req_q->num_requests; | ||||||
|  | 
 | ||||||
|  | 	if (mpi3mr_check_req_qfull(op_req_q)) { | ||||||
|  | 		midx = REPLY_QUEUE_IDX_TO_MSIX_IDX( | ||||||
|  | 		    reply_qidx, mrioc->op_reply_q_offset); | ||||||
|  | 		mpi3mr_process_op_reply_q(mrioc, &mrioc->intr_info[midx]); | ||||||
|  | 
 | ||||||
|  | 		if (mpi3mr_check_req_qfull(op_req_q)) { | ||||||
|  | 			retval = -EAGAIN; | ||||||
|  | 			goto out; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (mrioc->reset_in_progress) { | ||||||
|  | 		ioc_err(mrioc, "OpReqQ submit reset in progress\n"); | ||||||
|  | 		retval = -EAGAIN; | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	segment_base_addr = segments[pi / op_req_q->segment_qd].segment; | ||||||
|  | 	req_entry = (u8 *)segment_base_addr + | ||||||
|  | 	    ((pi % op_req_q->segment_qd) * req_sz); | ||||||
|  | 
 | ||||||
|  | 	memset(req_entry, 0, req_sz); | ||||||
|  | 	memcpy(req_entry, req, MPI3MR_ADMIN_REQ_FRAME_SZ); | ||||||
|  | 
 | ||||||
|  | 	if (++pi == max_entries) | ||||||
|  | 		pi = 0; | ||||||
|  | 	op_req_q->pi = pi; | ||||||
|  | 
 | ||||||
|  | 	writel(op_req_q->pi, | ||||||
|  | 	    &mrioc->sysif_regs->oper_queue_indexes[reply_qidx].producer_index); | ||||||
|  | 
 | ||||||
|  | out: | ||||||
|  | 	spin_unlock_irqrestore(&op_req_q->q_lock, flags); | ||||||
|  | 	return retval; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * mpi3mr_setup_admin_qpair - Setup admin queue pair |  * mpi3mr_setup_admin_qpair - Setup admin queue pair | ||||||
|  * @mrioc: Adapter instance reference |  * @mrioc: Adapter instance reference | ||||||
|  | @ -1887,6 +2048,89 @@ static int mpi3mr_alloc_chain_bufs(struct mpi3mr_ioc *mrioc) | ||||||
| 	return retval; | 	return retval; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * mpi3mr_port_enable_complete - Mark port enable complete | ||||||
|  |  * @mrioc: Adapter instance reference | ||||||
|  |  * @drv_cmd: Internal command tracker | ||||||
|  |  * | ||||||
|  |  * Call back for asynchronous port enable request sets the | ||||||
|  |  * driver command to indicate port enable request is complete. | ||||||
|  |  * | ||||||
|  |  * Return: Nothing | ||||||
|  |  */ | ||||||
|  | static void mpi3mr_port_enable_complete(struct mpi3mr_ioc *mrioc, | ||||||
|  | 	struct mpi3mr_drv_cmd *drv_cmd) | ||||||
|  | { | ||||||
|  | 	drv_cmd->state = MPI3MR_CMD_NOTUSED; | ||||||
|  | 	drv_cmd->callback = NULL; | ||||||
|  | 	mrioc->scan_failed = drv_cmd->ioc_status; | ||||||
|  | 	mrioc->scan_started = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * mpi3mr_issue_port_enable - Issue Port Enable | ||||||
|  |  * @mrioc: Adapter instance reference | ||||||
|  |  * @async: Flag to wait for completion or not | ||||||
|  |  * | ||||||
|  |  * Issue Port Enable MPI request through admin queue and if the | ||||||
|  |  * async flag is not set wait for the completion of the port | ||||||
|  |  * enable or time out. | ||||||
|  |  * | ||||||
|  |  * Return: 0 on success, non-zero on failures. | ||||||
|  |  */ | ||||||
|  | int mpi3mr_issue_port_enable(struct mpi3mr_ioc *mrioc, u8 async) | ||||||
|  | { | ||||||
|  | 	struct mpi3_port_enable_request pe_req; | ||||||
|  | 	int retval = 0; | ||||||
|  | 	u32 pe_timeout = MPI3MR_PORTENABLE_TIMEOUT; | ||||||
|  | 
 | ||||||
|  | 	memset(&pe_req, 0, sizeof(pe_req)); | ||||||
|  | 	mutex_lock(&mrioc->init_cmds.mutex); | ||||||
|  | 	if (mrioc->init_cmds.state & MPI3MR_CMD_PENDING) { | ||||||
|  | 		retval = -1; | ||||||
|  | 		ioc_err(mrioc, "Issue PortEnable: Init command is in use\n"); | ||||||
|  | 		mutex_unlock(&mrioc->init_cmds.mutex); | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 	mrioc->init_cmds.state = MPI3MR_CMD_PENDING; | ||||||
|  | 	if (async) { | ||||||
|  | 		mrioc->init_cmds.is_waiting = 0; | ||||||
|  | 		mrioc->init_cmds.callback = mpi3mr_port_enable_complete; | ||||||
|  | 	} else { | ||||||
|  | 		mrioc->init_cmds.is_waiting = 1; | ||||||
|  | 		mrioc->init_cmds.callback = NULL; | ||||||
|  | 		init_completion(&mrioc->init_cmds.done); | ||||||
|  | 	} | ||||||
|  | 	pe_req.host_tag = cpu_to_le16(MPI3MR_HOSTTAG_INITCMDS); | ||||||
|  | 	pe_req.function = MPI3_FUNCTION_PORT_ENABLE; | ||||||
|  | 
 | ||||||
|  | 	retval = mpi3mr_admin_request_post(mrioc, &pe_req, sizeof(pe_req), 1); | ||||||
|  | 	if (retval) { | ||||||
|  | 		ioc_err(mrioc, "Issue PortEnable: Admin Post failed\n"); | ||||||
|  | 		goto out_unlock; | ||||||
|  | 	} | ||||||
|  | 	if (!async) { | ||||||
|  | 		wait_for_completion_timeout(&mrioc->init_cmds.done, | ||||||
|  | 		    (pe_timeout * HZ)); | ||||||
|  | 		if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) { | ||||||
|  | 			ioc_err(mrioc, "Issue PortEnable: command timed out\n"); | ||||||
|  | 			retval = -1; | ||||||
|  | 			mrioc->scan_failed = MPI3_IOCSTATUS_INTERNAL_ERROR; | ||||||
|  | 			mpi3mr_set_diagsave(mrioc); | ||||||
|  | 			mpi3mr_issue_reset(mrioc, | ||||||
|  | 			    MPI3_SYSIF_HOST_DIAG_RESET_ACTION_DIAG_FAULT, | ||||||
|  | 			    MPI3MR_RESET_FROM_PE_TIMEOUT); | ||||||
|  | 			mrioc->unrecoverable = 1; | ||||||
|  | 			goto out_unlock; | ||||||
|  | 		} | ||||||
|  | 		mpi3mr_port_enable_complete(mrioc, &mrioc->init_cmds); | ||||||
|  | 	} | ||||||
|  | out_unlock: | ||||||
|  | 	mutex_unlock(&mrioc->init_cmds.mutex); | ||||||
|  | out: | ||||||
|  | 	return retval; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * mpi3mr_cleanup_resources - Free PCI resources |  * mpi3mr_cleanup_resources - Free PCI resources | ||||||
|  * @mrioc: Adapter instance reference |  * @mrioc: Adapter instance reference | ||||||
|  |  | ||||||
|  | @ -26,6 +26,462 @@ module_param(logging_level, int, 0); | ||||||
| MODULE_PARM_DESC(logging_level, | MODULE_PARM_DESC(logging_level, | ||||||
| 	" bits for enabling additional logging info (default=0)"); | 	" bits for enabling additional logging info (default=0)"); | ||||||
| 
 | 
 | ||||||
|  | /* Forward declarations*/ | ||||||
|  | /**
 | ||||||
|  |  * mpi3mr_host_tag_for_scmd - Get host tag for a scmd | ||||||
|  |  * @mrioc: Adapter instance reference | ||||||
|  |  * @scmd: SCSI command reference | ||||||
|  |  * | ||||||
|  |  * Calculate the host tag based on block tag for a given scmd. | ||||||
|  |  * | ||||||
|  |  * Return: Valid host tag or MPI3MR_HOSTTAG_INVALID. | ||||||
|  |  */ | ||||||
|  | static u16 mpi3mr_host_tag_for_scmd(struct mpi3mr_ioc *mrioc, | ||||||
|  | 	struct scsi_cmnd *scmd) | ||||||
|  | { | ||||||
|  | 	struct scmd_priv *priv = NULL; | ||||||
|  | 	u32 unique_tag; | ||||||
|  | 	u16 host_tag, hw_queue; | ||||||
|  | 
 | ||||||
|  | 	unique_tag = blk_mq_unique_tag(scmd->request); | ||||||
|  | 
 | ||||||
|  | 	hw_queue = blk_mq_unique_tag_to_hwq(unique_tag); | ||||||
|  | 	if (hw_queue >= mrioc->num_op_reply_q) | ||||||
|  | 		return MPI3MR_HOSTTAG_INVALID; | ||||||
|  | 	host_tag = blk_mq_unique_tag_to_tag(unique_tag); | ||||||
|  | 
 | ||||||
|  | 	if (WARN_ON(host_tag >= mrioc->max_host_ios)) | ||||||
|  | 		return MPI3MR_HOSTTAG_INVALID; | ||||||
|  | 
 | ||||||
|  | 	priv = scsi_cmd_priv(scmd); | ||||||
|  | 	/*host_tag 0 is invalid hence incrementing by 1*/ | ||||||
|  | 	priv->host_tag = host_tag + 1; | ||||||
|  | 	priv->scmd = scmd; | ||||||
|  | 	priv->in_lld_scope = 1; | ||||||
|  | 	priv->req_q_idx = hw_queue; | ||||||
|  | 	priv->chain_idx = -1; | ||||||
|  | 	return priv->host_tag; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * mpi3mr_scmd_from_host_tag - Get SCSI command from host tag | ||||||
|  |  * @mrioc: Adapter instance reference | ||||||
|  |  * @host_tag: Host tag | ||||||
|  |  * @qidx: Operational queue index | ||||||
|  |  * | ||||||
|  |  * Identify the block tag from the host tag and queue index and | ||||||
|  |  * retrieve associated scsi command using scsi_host_find_tag(). | ||||||
|  |  * | ||||||
|  |  * Return: SCSI command reference or NULL. | ||||||
|  |  */ | ||||||
|  | static struct scsi_cmnd *mpi3mr_scmd_from_host_tag( | ||||||
|  | 	struct mpi3mr_ioc *mrioc, u16 host_tag, u16 qidx) | ||||||
|  | { | ||||||
|  | 	struct scsi_cmnd *scmd = NULL; | ||||||
|  | 	struct scmd_priv *priv = NULL; | ||||||
|  | 	u32 unique_tag = host_tag - 1; | ||||||
|  | 
 | ||||||
|  | 	if (WARN_ON(host_tag > mrioc->max_host_ios)) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	unique_tag |= (qidx << BLK_MQ_UNIQUE_TAG_BITS); | ||||||
|  | 
 | ||||||
|  | 	scmd = scsi_host_find_tag(mrioc->shost, unique_tag); | ||||||
|  | 	if (scmd) { | ||||||
|  | 		priv = scsi_cmd_priv(scmd); | ||||||
|  | 		if (!priv->in_lld_scope) | ||||||
|  | 			scmd = NULL; | ||||||
|  | 	} | ||||||
|  | out: | ||||||
|  | 	return scmd; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * mpi3mr_clear_scmd_priv - Cleanup SCSI command private date | ||||||
|  |  * @mrioc: Adapter instance reference | ||||||
|  |  * @scmd: SCSI command reference | ||||||
|  |  * | ||||||
|  |  * Invalidate the SCSI command private data to mark the command | ||||||
|  |  * is not in LLD scope anymore. | ||||||
|  |  * | ||||||
|  |  * Return: Nothing. | ||||||
|  |  */ | ||||||
|  | static void mpi3mr_clear_scmd_priv(struct mpi3mr_ioc *mrioc, | ||||||
|  | 	struct scsi_cmnd *scmd) | ||||||
|  | { | ||||||
|  | 	struct scmd_priv *priv = NULL; | ||||||
|  | 
 | ||||||
|  | 	priv = scsi_cmd_priv(scmd); | ||||||
|  | 
 | ||||||
|  | 	if (WARN_ON(priv->in_lld_scope == 0)) | ||||||
|  | 		return; | ||||||
|  | 	priv->host_tag = MPI3MR_HOSTTAG_INVALID; | ||||||
|  | 	priv->req_q_idx = 0xFFFF; | ||||||
|  | 	priv->scmd = NULL; | ||||||
|  | 	priv->in_lld_scope = 0; | ||||||
|  | 	if (priv->chain_idx >= 0) { | ||||||
|  | 		clear_bit(priv->chain_idx, mrioc->chain_bitmap); | ||||||
|  | 		priv->chain_idx = -1; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * mpi3mr_process_op_reply_desc - reply descriptor handler | ||||||
|  |  * @mrioc: Adapter instance reference | ||||||
|  |  * @reply_desc: Operational reply descriptor | ||||||
|  |  * @reply_dma: place holder for reply DMA address | ||||||
|  |  * @qidx: Operational queue index | ||||||
|  |  * | ||||||
|  |  * Process the operational reply descriptor and identifies the | ||||||
|  |  * descriptor type. Based on the descriptor map the MPI3 request | ||||||
|  |  * status to a SCSI command status and calls scsi_done call | ||||||
|  |  * back. | ||||||
|  |  * | ||||||
|  |  * Return: Nothing | ||||||
|  |  */ | ||||||
|  | void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc, | ||||||
|  | 	struct mpi3_default_reply_descriptor *reply_desc, u64 *reply_dma, u16 qidx) | ||||||
|  | { | ||||||
|  | 	u16 reply_desc_type, host_tag = 0; | ||||||
|  | 	u16 ioc_status = MPI3_IOCSTATUS_SUCCESS; | ||||||
|  | 	u32 ioc_loginfo = 0; | ||||||
|  | 	struct mpi3_status_reply_descriptor *status_desc = NULL; | ||||||
|  | 	struct mpi3_address_reply_descriptor *addr_desc = NULL; | ||||||
|  | 	struct mpi3_success_reply_descriptor *success_desc = NULL; | ||||||
|  | 	struct mpi3_scsi_io_reply *scsi_reply = NULL; | ||||||
|  | 	struct scsi_cmnd *scmd = NULL; | ||||||
|  | 	struct scmd_priv *priv = NULL; | ||||||
|  | 	u8 *sense_buf = NULL; | ||||||
|  | 	u8 scsi_state = 0, scsi_status = 0, sense_state = 0; | ||||||
|  | 	u32 xfer_count = 0, sense_count = 0, resp_data = 0; | ||||||
|  | 	u16 dev_handle = 0xFFFF; | ||||||
|  | 	struct scsi_sense_hdr sshdr; | ||||||
|  | 
 | ||||||
|  | 	*reply_dma = 0; | ||||||
|  | 	reply_desc_type = le16_to_cpu(reply_desc->reply_flags) & | ||||||
|  | 	    MPI3_REPLY_DESCRIPT_FLAGS_TYPE_MASK; | ||||||
|  | 	switch (reply_desc_type) { | ||||||
|  | 	case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_STATUS: | ||||||
|  | 		status_desc = (struct mpi3_status_reply_descriptor *)reply_desc; | ||||||
|  | 		host_tag = le16_to_cpu(status_desc->host_tag); | ||||||
|  | 		ioc_status = le16_to_cpu(status_desc->ioc_status); | ||||||
|  | 		if (ioc_status & | ||||||
|  | 		    MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL) | ||||||
|  | 			ioc_loginfo = le32_to_cpu(status_desc->ioc_log_info); | ||||||
|  | 		ioc_status &= MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK; | ||||||
|  | 		break; | ||||||
|  | 	case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_ADDRESS_REPLY: | ||||||
|  | 		addr_desc = (struct mpi3_address_reply_descriptor *)reply_desc; | ||||||
|  | 		*reply_dma = le64_to_cpu(addr_desc->reply_frame_address); | ||||||
|  | 		scsi_reply = mpi3mr_get_reply_virt_addr(mrioc, | ||||||
|  | 		    *reply_dma); | ||||||
|  | 		if (!scsi_reply) { | ||||||
|  | 			panic("%s: scsi_reply is NULL, this shouldn't happen\n", | ||||||
|  | 			    mrioc->name); | ||||||
|  | 			goto out; | ||||||
|  | 		} | ||||||
|  | 		host_tag = le16_to_cpu(scsi_reply->host_tag); | ||||||
|  | 		ioc_status = le16_to_cpu(scsi_reply->ioc_status); | ||||||
|  | 		scsi_status = scsi_reply->scsi_status; | ||||||
|  | 		scsi_state = scsi_reply->scsi_state; | ||||||
|  | 		dev_handle = le16_to_cpu(scsi_reply->dev_handle); | ||||||
|  | 		sense_state = (scsi_state & MPI3_SCSI_STATE_SENSE_MASK); | ||||||
|  | 		xfer_count = le32_to_cpu(scsi_reply->transfer_count); | ||||||
|  | 		sense_count = le32_to_cpu(scsi_reply->sense_count); | ||||||
|  | 		resp_data = le32_to_cpu(scsi_reply->response_data); | ||||||
|  | 		sense_buf = mpi3mr_get_sensebuf_virt_addr(mrioc, | ||||||
|  | 		    le64_to_cpu(scsi_reply->sense_data_buffer_address)); | ||||||
|  | 		if (ioc_status & | ||||||
|  | 		    MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_LOGINFOAVAIL) | ||||||
|  | 			ioc_loginfo = le32_to_cpu(scsi_reply->ioc_log_info); | ||||||
|  | 		ioc_status &= MPI3_REPLY_DESCRIPT_STATUS_IOCSTATUS_STATUS_MASK; | ||||||
|  | 		if (sense_state == MPI3_SCSI_STATE_SENSE_BUFF_Q_EMPTY) | ||||||
|  | 			panic("%s: Ran out of sense buffers\n", mrioc->name); | ||||||
|  | 		break; | ||||||
|  | 	case MPI3_REPLY_DESCRIPT_FLAGS_TYPE_SUCCESS: | ||||||
|  | 		success_desc = (struct mpi3_success_reply_descriptor *)reply_desc; | ||||||
|  | 		host_tag = le16_to_cpu(success_desc->host_tag); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	scmd = mpi3mr_scmd_from_host_tag(mrioc, host_tag, qidx); | ||||||
|  | 	if (!scmd) { | ||||||
|  | 		panic("%s: Cannot Identify scmd for host_tag 0x%x\n", | ||||||
|  | 		    mrioc->name, host_tag); | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 	priv = scsi_cmd_priv(scmd); | ||||||
|  | 	if (success_desc) { | ||||||
|  | 		scmd->result = DID_OK << 16; | ||||||
|  | 		goto out_success; | ||||||
|  | 	} | ||||||
|  | 	if (ioc_status == MPI3_IOCSTATUS_SCSI_DATA_UNDERRUN && | ||||||
|  | 	    xfer_count == 0 && (scsi_status == MPI3_SCSI_STATUS_BUSY || | ||||||
|  | 	    scsi_status == MPI3_SCSI_STATUS_RESERVATION_CONFLICT || | ||||||
|  | 	    scsi_status == MPI3_SCSI_STATUS_TASK_SET_FULL)) | ||||||
|  | 		ioc_status = MPI3_IOCSTATUS_SUCCESS; | ||||||
|  | 
 | ||||||
|  | 	if ((sense_state == MPI3_SCSI_STATE_SENSE_VALID) && sense_count && | ||||||
|  | 	    sense_buf) { | ||||||
|  | 		u32 sz = min_t(u32, SCSI_SENSE_BUFFERSIZE, sense_count); | ||||||
|  | 
 | ||||||
|  | 		memcpy(scmd->sense_buffer, sense_buf, sz); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	switch (ioc_status) { | ||||||
|  | 	case MPI3_IOCSTATUS_BUSY: | ||||||
|  | 	case MPI3_IOCSTATUS_INSUFFICIENT_RESOURCES: | ||||||
|  | 		scmd->result = SAM_STAT_BUSY; | ||||||
|  | 		break; | ||||||
|  | 	case MPI3_IOCSTATUS_SCSI_DEVICE_NOT_THERE: | ||||||
|  | 		scmd->result = DID_NO_CONNECT << 16; | ||||||
|  | 		break; | ||||||
|  | 	case MPI3_IOCSTATUS_SCSI_IOC_TERMINATED: | ||||||
|  | 		scmd->result = DID_SOFT_ERROR << 16; | ||||||
|  | 		break; | ||||||
|  | 	case MPI3_IOCSTATUS_SCSI_TASK_TERMINATED: | ||||||
|  | 	case MPI3_IOCSTATUS_SCSI_EXT_TERMINATED: | ||||||
|  | 		scmd->result = DID_RESET << 16; | ||||||
|  | 		break; | ||||||
|  | 	case MPI3_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: | ||||||
|  | 		if ((xfer_count == 0) || (scmd->underflow > xfer_count)) | ||||||
|  | 			scmd->result = DID_SOFT_ERROR << 16; | ||||||
|  | 		else | ||||||
|  | 			scmd->result = (DID_OK << 16) | scsi_status; | ||||||
|  | 		break; | ||||||
|  | 	case MPI3_IOCSTATUS_SCSI_DATA_UNDERRUN: | ||||||
|  | 		scmd->result = (DID_OK << 16) | scsi_status; | ||||||
|  | 		if (sense_state == MPI3_SCSI_STATE_SENSE_VALID) | ||||||
|  | 			break; | ||||||
|  | 		if (xfer_count < scmd->underflow) { | ||||||
|  | 			if (scsi_status == SAM_STAT_BUSY) | ||||||
|  | 				scmd->result = SAM_STAT_BUSY; | ||||||
|  | 			else | ||||||
|  | 				scmd->result = DID_SOFT_ERROR << 16; | ||||||
|  | 		} else if ((scsi_state & (MPI3_SCSI_STATE_NO_SCSI_STATUS)) || | ||||||
|  | 		    (sense_state != MPI3_SCSI_STATE_SENSE_NOT_AVAILABLE)) | ||||||
|  | 			scmd->result = DID_SOFT_ERROR << 16; | ||||||
|  | 		else if (scsi_state & MPI3_SCSI_STATE_TERMINATED) | ||||||
|  | 			scmd->result = DID_RESET << 16; | ||||||
|  | 		break; | ||||||
|  | 	case MPI3_IOCSTATUS_SCSI_DATA_OVERRUN: | ||||||
|  | 		scsi_set_resid(scmd, 0); | ||||||
|  | 		fallthrough; | ||||||
|  | 	case MPI3_IOCSTATUS_SCSI_RECOVERED_ERROR: | ||||||
|  | 	case MPI3_IOCSTATUS_SUCCESS: | ||||||
|  | 		scmd->result = (DID_OK << 16) | scsi_status; | ||||||
|  | 		if ((scsi_state & (MPI3_SCSI_STATE_NO_SCSI_STATUS)) || | ||||||
|  | 			(sense_state == MPI3_SCSI_STATE_SENSE_FAILED) || | ||||||
|  | 			(sense_state == MPI3_SCSI_STATE_SENSE_BUFF_Q_EMPTY)) | ||||||
|  | 			scmd->result = DID_SOFT_ERROR << 16; | ||||||
|  | 		else if (scsi_state & MPI3_SCSI_STATE_TERMINATED) | ||||||
|  | 			scmd->result = DID_RESET << 16; | ||||||
|  | 		break; | ||||||
|  | 	case MPI3_IOCSTATUS_SCSI_PROTOCOL_ERROR: | ||||||
|  | 	case MPI3_IOCSTATUS_INVALID_FUNCTION: | ||||||
|  | 	case MPI3_IOCSTATUS_INVALID_SGL: | ||||||
|  | 	case MPI3_IOCSTATUS_INTERNAL_ERROR: | ||||||
|  | 	case MPI3_IOCSTATUS_INVALID_FIELD: | ||||||
|  | 	case MPI3_IOCSTATUS_INVALID_STATE: | ||||||
|  | 	case MPI3_IOCSTATUS_SCSI_IO_DATA_ERROR: | ||||||
|  | 	case MPI3_IOCSTATUS_SCSI_TASK_MGMT_FAILED: | ||||||
|  | 	case MPI3_IOCSTATUS_INSUFFICIENT_POWER: | ||||||
|  | 	default: | ||||||
|  | 		scmd->result = DID_SOFT_ERROR << 16; | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (scmd->result != (DID_OK << 16) && (scmd->cmnd[0] != ATA_12) && | ||||||
|  | 	    (scmd->cmnd[0] != ATA_16)) { | ||||||
|  | 		ioc_info(mrioc, "%s :scmd->result 0x%x\n", __func__, | ||||||
|  | 		    scmd->result); | ||||||
|  | 		scsi_print_command(scmd); | ||||||
|  | 		ioc_info(mrioc, | ||||||
|  | 		    "%s :Command issued to handle 0x%02x returned with error 0x%04x loginfo 0x%08x, qid %d\n", | ||||||
|  | 		    __func__, dev_handle, ioc_status, ioc_loginfo, | ||||||
|  | 		    priv->req_q_idx + 1); | ||||||
|  | 		ioc_info(mrioc, | ||||||
|  | 		    " host_tag %d scsi_state 0x%02x scsi_status 0x%02x, xfer_cnt %d resp_data 0x%x\n", | ||||||
|  | 		    host_tag, scsi_state, scsi_status, xfer_count, resp_data); | ||||||
|  | 		if (sense_buf) { | ||||||
|  | 			scsi_normalize_sense(sense_buf, sense_count, &sshdr); | ||||||
|  | 			ioc_info(mrioc, | ||||||
|  | 			    "%s :sense_count 0x%x, sense_key 0x%x ASC 0x%x, ASCQ 0x%x\n", | ||||||
|  | 			    __func__, sense_count, sshdr.sense_key, | ||||||
|  | 			    sshdr.asc, sshdr.ascq); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | out_success: | ||||||
|  | 	mpi3mr_clear_scmd_priv(mrioc, scmd); | ||||||
|  | 	scsi_dma_unmap(scmd); | ||||||
|  | 	scmd->scsi_done(scmd); | ||||||
|  | out: | ||||||
|  | 	if (sense_buf) | ||||||
|  | 		mpi3mr_repost_sense_buf(mrioc, | ||||||
|  | 		    le64_to_cpu(scsi_reply->sense_data_buffer_address)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * mpi3mr_get_chain_idx - get free chain buffer index | ||||||
|  |  * @mrioc: Adapter instance reference | ||||||
|  |  * | ||||||
|  |  * Try to get a free chain buffer index from the free pool. | ||||||
|  |  * | ||||||
|  |  * Return: -1 on failure or the free chain buffer index | ||||||
|  |  */ | ||||||
|  | static int mpi3mr_get_chain_idx(struct mpi3mr_ioc *mrioc) | ||||||
|  | { | ||||||
|  | 	u8 retry_count = 5; | ||||||
|  | 	int cmd_idx = -1; | ||||||
|  | 
 | ||||||
|  | 	do { | ||||||
|  | 		spin_lock(&mrioc->chain_buf_lock); | ||||||
|  | 		cmd_idx = find_first_zero_bit(mrioc->chain_bitmap, | ||||||
|  | 		    mrioc->chain_buf_count); | ||||||
|  | 		if (cmd_idx < mrioc->chain_buf_count) { | ||||||
|  | 			set_bit(cmd_idx, mrioc->chain_bitmap); | ||||||
|  | 			spin_unlock(&mrioc->chain_buf_lock); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		spin_unlock(&mrioc->chain_buf_lock); | ||||||
|  | 		cmd_idx = -1; | ||||||
|  | 	} while (retry_count--); | ||||||
|  | 	return cmd_idx; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * mpi3mr_prepare_sg_scmd - build scatter gather list | ||||||
|  |  * @mrioc: Adapter instance reference | ||||||
|  |  * @scmd: SCSI command reference | ||||||
|  |  * @scsiio_req: MPI3 SCSI IO request | ||||||
|  |  * | ||||||
|  |  * This function maps SCSI command's data and protection SGEs to | ||||||
|  |  * MPI request SGEs. If required additional 4K chain buffer is | ||||||
|  |  * used to send the SGEs. | ||||||
|  |  * | ||||||
|  |  * Return: 0 on success, -ENOMEM on dma_map_sg failure | ||||||
|  |  */ | ||||||
|  | static int mpi3mr_prepare_sg_scmd(struct mpi3mr_ioc *mrioc, | ||||||
|  | 	struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req) | ||||||
|  | { | ||||||
|  | 	dma_addr_t chain_dma; | ||||||
|  | 	struct scatterlist *sg_scmd; | ||||||
|  | 	void *sg_local, *chain; | ||||||
|  | 	u32 chain_length; | ||||||
|  | 	int sges_left, chain_idx; | ||||||
|  | 	u32 sges_in_segment; | ||||||
|  | 	u8 simple_sgl_flags; | ||||||
|  | 	u8 simple_sgl_flags_last; | ||||||
|  | 	u8 last_chain_sgl_flags; | ||||||
|  | 	struct chain_element *chain_req; | ||||||
|  | 	struct scmd_priv *priv = NULL; | ||||||
|  | 
 | ||||||
|  | 	priv = scsi_cmd_priv(scmd); | ||||||
|  | 
 | ||||||
|  | 	simple_sgl_flags = MPI3_SGE_FLAGS_ELEMENT_TYPE_SIMPLE | | ||||||
|  | 	    MPI3_SGE_FLAGS_DLAS_SYSTEM; | ||||||
|  | 	simple_sgl_flags_last = simple_sgl_flags | | ||||||
|  | 	    MPI3_SGE_FLAGS_END_OF_LIST; | ||||||
|  | 	last_chain_sgl_flags = MPI3_SGE_FLAGS_ELEMENT_TYPE_LAST_CHAIN | | ||||||
|  | 	    MPI3_SGE_FLAGS_DLAS_SYSTEM; | ||||||
|  | 
 | ||||||
|  | 	sg_local = &scsiio_req->sgl; | ||||||
|  | 
 | ||||||
|  | 	if (!scsiio_req->data_length) { | ||||||
|  | 		mpi3mr_build_zero_len_sge(sg_local); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	sg_scmd = scsi_sglist(scmd); | ||||||
|  | 	sges_left = scsi_dma_map(scmd); | ||||||
|  | 
 | ||||||
|  | 	if (sges_left < 0) { | ||||||
|  | 		sdev_printk(KERN_ERR, scmd->device, | ||||||
|  | 		    "scsi_dma_map failed: request for %d bytes!\n", | ||||||
|  | 		    scsi_bufflen(scmd)); | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 	} | ||||||
|  | 	if (sges_left > MPI3MR_SG_DEPTH) { | ||||||
|  | 		sdev_printk(KERN_ERR, scmd->device, | ||||||
|  | 		    "scsi_dma_map returned unsupported sge count %d!\n", | ||||||
|  | 		    sges_left); | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	sges_in_segment = (mrioc->facts.op_req_sz - | ||||||
|  | 	    offsetof(struct mpi3_scsi_io_request, sgl)) / sizeof(struct mpi3_sge_common); | ||||||
|  | 
 | ||||||
|  | 	if (sges_left <= sges_in_segment) | ||||||
|  | 		goto fill_in_last_segment; | ||||||
|  | 
 | ||||||
|  | 	/* fill in main message segment when there is a chain following */ | ||||||
|  | 	while (sges_in_segment > 1) { | ||||||
|  | 		mpi3mr_add_sg_single(sg_local, simple_sgl_flags, | ||||||
|  | 		    sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); | ||||||
|  | 		sg_scmd = sg_next(sg_scmd); | ||||||
|  | 		sg_local += sizeof(struct mpi3_sge_common); | ||||||
|  | 		sges_left--; | ||||||
|  | 		sges_in_segment--; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	chain_idx = mpi3mr_get_chain_idx(mrioc); | ||||||
|  | 	if (chain_idx < 0) | ||||||
|  | 		return -1; | ||||||
|  | 	chain_req = &mrioc->chain_sgl_list[chain_idx]; | ||||||
|  | 	priv->chain_idx = chain_idx; | ||||||
|  | 
 | ||||||
|  | 	chain = chain_req->addr; | ||||||
|  | 	chain_dma = chain_req->dma_addr; | ||||||
|  | 	sges_in_segment = sges_left; | ||||||
|  | 	chain_length = sges_in_segment * sizeof(struct mpi3_sge_common); | ||||||
|  | 
 | ||||||
|  | 	mpi3mr_add_sg_single(sg_local, last_chain_sgl_flags, | ||||||
|  | 	    chain_length, chain_dma); | ||||||
|  | 
 | ||||||
|  | 	sg_local = chain; | ||||||
|  | 
 | ||||||
|  | fill_in_last_segment: | ||||||
|  | 	while (sges_left > 0) { | ||||||
|  | 		if (sges_left == 1) | ||||||
|  | 			mpi3mr_add_sg_single(sg_local, | ||||||
|  | 			    simple_sgl_flags_last, sg_dma_len(sg_scmd), | ||||||
|  | 			    sg_dma_address(sg_scmd)); | ||||||
|  | 		else | ||||||
|  | 			mpi3mr_add_sg_single(sg_local, simple_sgl_flags, | ||||||
|  | 			    sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); | ||||||
|  | 		sg_scmd = sg_next(sg_scmd); | ||||||
|  | 		sg_local += sizeof(struct mpi3_sge_common); | ||||||
|  | 		sges_left--; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * mpi3mr_build_sg_scmd - build scatter gather list for SCSI IO | ||||||
|  |  * @mrioc: Adapter instance reference | ||||||
|  |  * @scmd: SCSI command reference | ||||||
|  |  * @scsiio_req: MPI3 SCSI IO request | ||||||
|  |  * | ||||||
|  |  * This function calls mpi3mr_prepare_sg_scmd for constructing | ||||||
|  |  * both data SGEs and protection information SGEs in the MPI | ||||||
|  |  * format from the SCSI Command as appropriate . | ||||||
|  |  * | ||||||
|  |  * Return: return value of mpi3mr_prepare_sg_scmd. | ||||||
|  |  */ | ||||||
|  | static int mpi3mr_build_sg_scmd(struct mpi3mr_ioc *mrioc, | ||||||
|  | 	struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = mpi3mr_prepare_sg_scmd(mrioc, scmd, scsiio_req); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * mpi3mr_map_queues - Map queues callback handler |  * mpi3mr_map_queues - Map queues callback handler | ||||||
|  * @shost: SCSI host reference |  * @shost: SCSI host reference | ||||||
|  | @ -43,6 +499,71 @@ static int mpi3mr_map_queues(struct Scsi_Host *shost) | ||||||
| 	    mrioc->pdev, mrioc->op_reply_q_offset); | 	    mrioc->pdev, mrioc->op_reply_q_offset); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * mpi3mr_scan_start - Scan start callback handler | ||||||
|  |  * @shost: SCSI host reference | ||||||
|  |  * | ||||||
|  |  * Issue port enable request asynchronously. | ||||||
|  |  * | ||||||
|  |  * Return: Nothing | ||||||
|  |  */ | ||||||
|  | static void mpi3mr_scan_start(struct Scsi_Host *shost) | ||||||
|  | { | ||||||
|  | 	struct mpi3mr_ioc *mrioc = shost_priv(shost); | ||||||
|  | 
 | ||||||
|  | 	mrioc->scan_started = 1; | ||||||
|  | 	ioc_info(mrioc, "%s :Issuing Port Enable\n", __func__); | ||||||
|  | 	if (mpi3mr_issue_port_enable(mrioc, 1)) { | ||||||
|  | 		ioc_err(mrioc, "%s :Issuing port enable failed\n", __func__); | ||||||
|  | 		mrioc->scan_started = 0; | ||||||
|  | 		mrioc->scan_failed = MPI3_IOCSTATUS_INTERNAL_ERROR; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * mpi3mr_scan_finished - Scan finished callback handler | ||||||
|  |  * @shost: SCSI host reference | ||||||
|  |  * @time: Jiffies from the scan start | ||||||
|  |  * | ||||||
|  |  * Checks whether the port enable is completed or timedout or | ||||||
|  |  * failed and set the scan status accordingly after taking any | ||||||
|  |  * recovery if required. | ||||||
|  |  * | ||||||
|  |  * Return: 1 on scan finished or timed out, 0 for in progress | ||||||
|  |  */ | ||||||
|  | static int mpi3mr_scan_finished(struct Scsi_Host *shost, | ||||||
|  | 	unsigned long time) | ||||||
|  | { | ||||||
|  | 	struct mpi3mr_ioc *mrioc = shost_priv(shost); | ||||||
|  | 	u32 pe_timeout = MPI3MR_PORTENABLE_TIMEOUT; | ||||||
|  | 
 | ||||||
|  | 	if (time >= (pe_timeout * HZ)) { | ||||||
|  | 		mrioc->init_cmds.is_waiting = 0; | ||||||
|  | 		mrioc->init_cmds.callback = NULL; | ||||||
|  | 		mrioc->init_cmds.state = MPI3MR_CMD_NOTUSED; | ||||||
|  | 		ioc_err(mrioc, "%s :port enable request timed out\n", __func__); | ||||||
|  | 		mrioc->is_driver_loading = 0; | ||||||
|  | 		mpi3mr_soft_reset_handler(mrioc, | ||||||
|  | 		    MPI3MR_RESET_FROM_PE_TIMEOUT, 1); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (mrioc->scan_failed) { | ||||||
|  | 		ioc_err(mrioc, | ||||||
|  | 		    "%s :port enable failed with (ioc_status=0x%08x)\n", | ||||||
|  | 		    __func__, mrioc->scan_failed); | ||||||
|  | 		mrioc->is_driver_loading = 0; | ||||||
|  | 		mrioc->stop_drv_processing = 1; | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (mrioc->scan_started) | ||||||
|  | 		return 0; | ||||||
|  | 	ioc_info(mrioc, "%s :port enable: SUCCESS\n", __func__); | ||||||
|  | 	mrioc->is_driver_loading = 0; | ||||||
|  | 
 | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * mpi3mr_slave_destroy - Slave destroy callback handler |  * mpi3mr_slave_destroy - Slave destroy callback handler | ||||||
|  * @sdev: SCSI device reference |  * @sdev: SCSI device reference | ||||||
|  | @ -125,10 +646,114 @@ static int mpi3mr_target_alloc(struct scsi_target *starget) | ||||||
| static int mpi3mr_qcmd(struct Scsi_Host *shost, | static int mpi3mr_qcmd(struct Scsi_Host *shost, | ||||||
| 	struct scsi_cmnd *scmd) | 	struct scsi_cmnd *scmd) | ||||||
| { | { | ||||||
|  | 	struct mpi3mr_ioc *mrioc = shost_priv(shost); | ||||||
|  | 	struct mpi3mr_stgt_priv_data *stgt_priv_data; | ||||||
|  | 	struct mpi3mr_sdev_priv_data *sdev_priv_data; | ||||||
|  | 	struct scmd_priv *scmd_priv_data = NULL; | ||||||
|  | 	struct mpi3_scsi_io_request *scsiio_req = NULL; | ||||||
|  | 	struct op_req_qinfo *op_req_q = NULL; | ||||||
| 	int retval = 0; | 	int retval = 0; | ||||||
|  | 	u16 dev_handle; | ||||||
|  | 	u16 host_tag; | ||||||
|  | 	u32 scsiio_flags = 0; | ||||||
|  | 	struct request *rq = scmd->request; | ||||||
|  | 	int iprio_class; | ||||||
| 
 | 
 | ||||||
| 	scmd->result = DID_NO_CONNECT << 16; | 	sdev_priv_data = scmd->device->hostdata; | ||||||
| 	scmd->scsi_done(scmd); | 	if (!sdev_priv_data || !sdev_priv_data->tgt_priv_data) { | ||||||
|  | 		scmd->result = DID_NO_CONNECT << 16; | ||||||
|  | 		scmd->scsi_done(scmd); | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (mrioc->stop_drv_processing) { | ||||||
|  | 		scmd->result = DID_NO_CONNECT << 16; | ||||||
|  | 		scmd->scsi_done(scmd); | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (mrioc->reset_in_progress) { | ||||||
|  | 		retval = SCSI_MLQUEUE_HOST_BUSY; | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	stgt_priv_data = sdev_priv_data->tgt_priv_data; | ||||||
|  | 
 | ||||||
|  | 	dev_handle = stgt_priv_data->dev_handle; | ||||||
|  | 	if (dev_handle == MPI3MR_INVALID_DEV_HANDLE) { | ||||||
|  | 		scmd->result = DID_NO_CONNECT << 16; | ||||||
|  | 		scmd->scsi_done(scmd); | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 	if (stgt_priv_data->dev_removed) { | ||||||
|  | 		scmd->result = DID_NO_CONNECT << 16; | ||||||
|  | 		scmd->scsi_done(scmd); | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (atomic_read(&stgt_priv_data->block_io)) { | ||||||
|  | 		if (mrioc->stop_drv_processing) { | ||||||
|  | 			scmd->result = DID_NO_CONNECT << 16; | ||||||
|  | 			scmd->scsi_done(scmd); | ||||||
|  | 			goto out; | ||||||
|  | 		} | ||||||
|  | 		retval = SCSI_MLQUEUE_DEVICE_BUSY; | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	host_tag = mpi3mr_host_tag_for_scmd(mrioc, scmd); | ||||||
|  | 	if (host_tag == MPI3MR_HOSTTAG_INVALID) { | ||||||
|  | 		scmd->result = DID_ERROR << 16; | ||||||
|  | 		scmd->scsi_done(scmd); | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (scmd->sc_data_direction == DMA_FROM_DEVICE) | ||||||
|  | 		scsiio_flags = MPI3_SCSIIO_FLAGS_DATADIRECTION_READ; | ||||||
|  | 	else if (scmd->sc_data_direction == DMA_TO_DEVICE) | ||||||
|  | 		scsiio_flags = MPI3_SCSIIO_FLAGS_DATADIRECTION_WRITE; | ||||||
|  | 	else | ||||||
|  | 		scsiio_flags = MPI3_SCSIIO_FLAGS_DATADIRECTION_NO_DATA_TRANSFER; | ||||||
|  | 
 | ||||||
|  | 	scsiio_flags |= MPI3_SCSIIO_FLAGS_TASKATTRIBUTE_SIMPLEQ; | ||||||
|  | 
 | ||||||
|  | 	if (sdev_priv_data->ncq_prio_enable) { | ||||||
|  | 		iprio_class = IOPRIO_PRIO_CLASS(req_get_ioprio(rq)); | ||||||
|  | 		if (iprio_class == IOPRIO_CLASS_RT) | ||||||
|  | 			scsiio_flags |= 1 << MPI3_SCSIIO_FLAGS_CMDPRI_SHIFT; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (scmd->cmd_len > 16) | ||||||
|  | 		scsiio_flags |= MPI3_SCSIIO_FLAGS_CDB_GREATER_THAN_16; | ||||||
|  | 
 | ||||||
|  | 	scmd_priv_data = scsi_cmd_priv(scmd); | ||||||
|  | 	memset(scmd_priv_data->mpi3mr_scsiio_req, 0, MPI3MR_ADMIN_REQ_FRAME_SZ); | ||||||
|  | 	scsiio_req = (struct mpi3_scsi_io_request *)scmd_priv_data->mpi3mr_scsiio_req; | ||||||
|  | 	scsiio_req->function = MPI3_FUNCTION_SCSI_IO; | ||||||
|  | 	scsiio_req->host_tag = cpu_to_le16(host_tag); | ||||||
|  | 
 | ||||||
|  | 	memcpy(scsiio_req->cdb.cdb32, scmd->cmnd, scmd->cmd_len); | ||||||
|  | 	scsiio_req->data_length = cpu_to_le32(scsi_bufflen(scmd)); | ||||||
|  | 	scsiio_req->dev_handle = cpu_to_le16(dev_handle); | ||||||
|  | 	scsiio_req->flags = cpu_to_le32(scsiio_flags); | ||||||
|  | 	int_to_scsilun(sdev_priv_data->lun_id, | ||||||
|  | 	    (struct scsi_lun *)scsiio_req->lun); | ||||||
|  | 
 | ||||||
|  | 	if (mpi3mr_build_sg_scmd(mrioc, scmd, scsiio_req)) { | ||||||
|  | 		mpi3mr_clear_scmd_priv(mrioc, scmd); | ||||||
|  | 		retval = SCSI_MLQUEUE_HOST_BUSY; | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 	op_req_q = &mrioc->req_qinfo[scmd_priv_data->req_q_idx]; | ||||||
|  | 
 | ||||||
|  | 	if (mpi3mr_op_request_post(mrioc, op_req_q, | ||||||
|  | 	    scmd_priv_data->mpi3mr_scsiio_req)) { | ||||||
|  | 		mpi3mr_clear_scmd_priv(mrioc, scmd); | ||||||
|  | 		retval = SCSI_MLQUEUE_HOST_BUSY; | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | out: | ||||||
| 	return retval; | 	return retval; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -142,6 +767,8 @@ static struct scsi_host_template mpi3mr_driver_template = { | ||||||
| 	.slave_configure		= mpi3mr_slave_configure, | 	.slave_configure		= mpi3mr_slave_configure, | ||||||
| 	.target_destroy			= mpi3mr_target_destroy, | 	.target_destroy			= mpi3mr_target_destroy, | ||||||
| 	.slave_destroy			= mpi3mr_slave_destroy, | 	.slave_destroy			= mpi3mr_slave_destroy, | ||||||
|  | 	.scan_finished			= mpi3mr_scan_finished, | ||||||
|  | 	.scan_start			= mpi3mr_scan_start, | ||||||
| 	.map_queues			= mpi3mr_map_queues, | 	.map_queues			= mpi3mr_map_queues, | ||||||
| 	.no_write_same			= 1, | 	.no_write_same			= 1, | ||||||
| 	.can_queue			= 1, | 	.can_queue			= 1, | ||||||
|  | @ -216,6 +843,7 @@ mpi3mr_probe(struct pci_dev *pdev, const struct pci_device_id *id) | ||||||
| 	spin_lock_init(&mrioc->admin_req_lock); | 	spin_lock_init(&mrioc->admin_req_lock); | ||||||
| 	spin_lock_init(&mrioc->reply_free_queue_lock); | 	spin_lock_init(&mrioc->reply_free_queue_lock); | ||||||
| 	spin_lock_init(&mrioc->sbq_lock); | 	spin_lock_init(&mrioc->sbq_lock); | ||||||
|  | 	spin_lock_init(&mrioc->chain_buf_lock); | ||||||
| 
 | 
 | ||||||
| 	mpi3mr_init_drv_cmd(&mrioc->init_cmds, MPI3MR_HOSTTAG_INITCMDS); | 	mpi3mr_init_drv_cmd(&mrioc->init_cmds, MPI3MR_HOSTTAG_INITCMDS); | ||||||
| 	if (pdev->revision) | 	if (pdev->revision) | ||||||
|  | @ -285,6 +913,7 @@ static void mpi3mr_remove(struct pci_dev *pdev) | ||||||
| 	while (mrioc->reset_in_progress || mrioc->is_driver_loading) | 	while (mrioc->reset_in_progress || mrioc->is_driver_loading) | ||||||
| 		ssleep(1); | 		ssleep(1); | ||||||
| 
 | 
 | ||||||
|  | 	mrioc->stop_drv_processing = 1; | ||||||
| 	scsi_remove_host(shost); | 	scsi_remove_host(shost); | ||||||
| 
 | 
 | ||||||
| 	mpi3mr_cleanup_ioc(mrioc); | 	mpi3mr_cleanup_ioc(mrioc); | ||||||
|  | @ -317,6 +946,7 @@ static void mpi3mr_shutdown(struct pci_dev *pdev) | ||||||
| 	while (mrioc->reset_in_progress || mrioc->is_driver_loading) | 	while (mrioc->reset_in_progress || mrioc->is_driver_loading) | ||||||
| 		ssleep(1); | 		ssleep(1); | ||||||
| 
 | 
 | ||||||
|  | 	mrioc->stop_drv_processing = 1; | ||||||
| 	mpi3mr_cleanup_ioc(mrioc); | 	mpi3mr_cleanup_ioc(mrioc); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Kashyap Desai
						Kashyap Desai