diff options
Diffstat (limited to 'drivers/scsi/isci/remote_device.c')
| -rw-r--r-- | drivers/scsi/isci/remote_device.c | 576 | 
1 files changed, 433 insertions, 143 deletions
diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index 8f501b0a81d..c3aa6c5457b 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c @@ -72,46 +72,11 @@ const char *dev_state_name(enum sci_remote_device_states state)  }  #undef C -/** - * isci_remote_device_not_ready() - This function is called by the ihost when - *    the remote device is not ready. We mark the isci device as ready (not - *    "ready_for_io") and signal the waiting proccess. - * @isci_host: This parameter specifies the isci host object. - * @isci_device: This parameter specifies the remote device - * - * sci_lock is held on entrance to this function. - */ -static void isci_remote_device_not_ready(struct isci_host *ihost, -				  struct isci_remote_device *idev, u32 reason) +enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev, +					  enum sci_remote_node_suspension_reasons reason)  { -	struct isci_request *ireq; - -	dev_dbg(&ihost->pdev->dev, -		"%s: isci_device = %p\n", __func__, idev); - -	switch (reason) { -	case SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED: -		set_bit(IDEV_GONE, &idev->flags); -		break; -	case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED: -		set_bit(IDEV_IO_NCQERROR, &idev->flags); - -		/* Kill all outstanding requests for the device. */ -		list_for_each_entry(ireq, &idev->reqs_in_process, dev_node) { - -			dev_dbg(&ihost->pdev->dev, -				"%s: isci_device = %p request = %p\n", -				__func__, idev, ireq); - -			sci_controller_terminate_request(ihost, -							  idev, -							  ireq); -		} -		/* Fall through into the default case... */ -	default: -		clear_bit(IDEV_IO_READY, &idev->flags); -		break; -	} +	return sci_remote_node_context_suspend(&idev->rnc, reason, +					       SCI_SOFTWARE_SUSPEND_EXPECTED_EVENT);  }  /** @@ -133,18 +98,29 @@ static void isci_remote_device_ready(struct isci_host *ihost, struct isci_remote  		wake_up(&ihost->eventq);  } -/* called once the remote node context is ready to be freed. - * The remote device can now report that its stop operation is complete. none - */ -static void rnc_destruct_done(void *_dev) +static enum sci_status sci_remote_device_terminate_req( +	struct isci_host *ihost, +	struct isci_remote_device *idev, +	int check_abort, +	struct isci_request *ireq)  { -	struct isci_remote_device *idev = _dev; +	if (!test_bit(IREQ_ACTIVE, &ireq->flags) || +	    (ireq->target_device != idev) || +	    (check_abort && !test_bit(IREQ_PENDING_ABORT, &ireq->flags))) +		return SCI_SUCCESS; -	BUG_ON(idev->started_request_count != 0); -	sci_change_state(&idev->sm, SCI_DEV_STOPPED); +	dev_dbg(&ihost->pdev->dev, +		"%s: idev=%p; flags=%lx; req=%p; req target=%p\n", +		__func__, idev, idev->flags, ireq, ireq->target_device); + +	set_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags); + +	return sci_controller_terminate_request(ihost, idev, ireq);  } -static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_device *idev) +static enum sci_status sci_remote_device_terminate_reqs_checkabort( +	struct isci_remote_device *idev, +	int chk)  {  	struct isci_host *ihost = idev->owning_port->owning_controller;  	enum sci_status status  = SCI_SUCCESS; @@ -154,18 +130,210 @@ static enum sci_status sci_remote_device_terminate_requests(struct isci_remote_d  		struct isci_request *ireq = ihost->reqs[i];  		enum sci_status s; -		if (!test_bit(IREQ_ACTIVE, &ireq->flags) || -		    ireq->target_device != idev) -			continue; - -		s = sci_controller_terminate_request(ihost, idev, ireq); +		s = sci_remote_device_terminate_req(ihost, idev, chk, ireq);  		if (s != SCI_SUCCESS)  			status = s;  	} +	return status; +} + +static bool isci_compare_suspendcount( +	struct isci_remote_device *idev, +	u32 localcount) +{ +	smp_rmb(); + +	/* Check for a change in the suspend count, or the RNC +	 * being destroyed. +	 */ +	return (localcount != idev->rnc.suspend_count) +	    || sci_remote_node_context_is_being_destroyed(&idev->rnc); +} + +static bool isci_check_reqterm( +	struct isci_host *ihost, +	struct isci_remote_device *idev, +	struct isci_request *ireq, +	u32 localcount) +{ +	unsigned long flags; +	bool res; + +	spin_lock_irqsave(&ihost->scic_lock, flags); +	res = isci_compare_suspendcount(idev, localcount) +		&& !test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags); +	spin_unlock_irqrestore(&ihost->scic_lock, flags); + +	return res; +} + +static bool isci_check_devempty( +	struct isci_host *ihost, +	struct isci_remote_device *idev, +	u32 localcount) +{ +	unsigned long flags; +	bool res; + +	spin_lock_irqsave(&ihost->scic_lock, flags); +	res = isci_compare_suspendcount(idev, localcount) +		&& idev->started_request_count == 0; +	spin_unlock_irqrestore(&ihost->scic_lock, flags); + +	return res; +} + +enum sci_status isci_remote_device_terminate_requests( +	struct isci_host *ihost, +	struct isci_remote_device *idev, +	struct isci_request *ireq) +{ +	enum sci_status status = SCI_SUCCESS; +	unsigned long flags; +	u32 rnc_suspend_count; +	spin_lock_irqsave(&ihost->scic_lock, flags); + +	if (isci_get_device(idev) == NULL) { +		dev_dbg(&ihost->pdev->dev, "%s: failed isci_get_device(idev=%p)\n", +			__func__, idev); +		spin_unlock_irqrestore(&ihost->scic_lock, flags); +		status = SCI_FAILURE; +	} else { +		/* If already suspended, don't wait for another suspension. */ +		smp_rmb(); +		rnc_suspend_count +			= sci_remote_node_context_is_suspended(&idev->rnc) +				? 0 : idev->rnc.suspend_count; + +		dev_dbg(&ihost->pdev->dev, +			"%s: idev=%p, ireq=%p; started_request_count=%d, " +				"rnc_suspend_count=%d, rnc.suspend_count=%d" +				"about to wait\n", +			__func__, idev, ireq, idev->started_request_count, +			rnc_suspend_count, idev->rnc.suspend_count); + +		#define MAX_SUSPEND_MSECS 10000 +		if (ireq) { +			/* Terminate a specific TC. */ +			set_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags); +			sci_remote_device_terminate_req(ihost, idev, 0, ireq); +			spin_unlock_irqrestore(&ihost->scic_lock, flags); +			if (!wait_event_timeout(ihost->eventq, +						isci_check_reqterm(ihost, idev, ireq, +								   rnc_suspend_count), +						msecs_to_jiffies(MAX_SUSPEND_MSECS))) { + +				dev_warn(&ihost->pdev->dev, "%s host%d timeout single\n", +					 __func__, ihost->id); +				dev_dbg(&ihost->pdev->dev, +					 "%s: ******* Timeout waiting for " +					 "suspend; idev=%p, current state %s; " +					 "started_request_count=%d, flags=%lx\n\t" +					 "rnc_suspend_count=%d, rnc.suspend_count=%d " +					 "RNC: current state %s, current " +					 "suspend_type %x dest state %d;\n" +					 "ireq=%p, ireq->flags = %lx\n", +					 __func__, idev, +					 dev_state_name(idev->sm.current_state_id), +					 idev->started_request_count, idev->flags, +					 rnc_suspend_count, idev->rnc.suspend_count, +					 rnc_state_name(idev->rnc.sm.current_state_id), +					 idev->rnc.suspend_type, +					 idev->rnc.destination_state, +					 ireq, ireq->flags); +			} +			spin_lock_irqsave(&ihost->scic_lock, flags); +			clear_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags); +			if (!test_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags)) +				isci_free_tag(ihost, ireq->io_tag); +			spin_unlock_irqrestore(&ihost->scic_lock, flags); +		} else { +			/* Terminate all TCs. */ +			sci_remote_device_terminate_requests(idev); +			spin_unlock_irqrestore(&ihost->scic_lock, flags); +			if (!wait_event_timeout(ihost->eventq, +						isci_check_devempty(ihost, idev, +								    rnc_suspend_count), +						msecs_to_jiffies(MAX_SUSPEND_MSECS))) { + +				dev_warn(&ihost->pdev->dev, "%s host%d timeout all\n", +					 __func__, ihost->id); +				dev_dbg(&ihost->pdev->dev, +					"%s: ******* Timeout waiting for " +					"suspend; idev=%p, current state %s; " +					"started_request_count=%d, flags=%lx\n\t" +					"rnc_suspend_count=%d, " +					"RNC: current state %s, " +					"rnc.suspend_count=%d, current " +					"suspend_type %x dest state %d\n", +					__func__, idev, +					dev_state_name(idev->sm.current_state_id), +					idev->started_request_count, idev->flags, +					rnc_suspend_count, +					rnc_state_name(idev->rnc.sm.current_state_id), +					idev->rnc.suspend_count, +					idev->rnc.suspend_type, +					idev->rnc.destination_state); +			} +		} +		dev_dbg(&ihost->pdev->dev, "%s: idev=%p, wait done\n", +			__func__, idev); +		isci_put_device(idev); +	}  	return status;  } +/** +* isci_remote_device_not_ready() - This function is called by the ihost when +*    the remote device is not ready. We mark the isci device as ready (not +*    "ready_for_io") and signal the waiting proccess. +* @isci_host: This parameter specifies the isci host object. +* @isci_device: This parameter specifies the remote device +* +* sci_lock is held on entrance to this function. +*/ +static void isci_remote_device_not_ready(struct isci_host *ihost, +					 struct isci_remote_device *idev, +					 u32 reason) +{ +	dev_dbg(&ihost->pdev->dev, +		"%s: isci_device = %p; reason = %d\n", __func__, idev, reason); + +	switch (reason) { +	case SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED: +		set_bit(IDEV_IO_NCQERROR, &idev->flags); + +		/* Suspend the remote device so the I/O can be terminated. */ +		sci_remote_device_suspend(idev, SCI_SW_SUSPEND_NORMAL); + +		/* Kill all outstanding requests for the device. */ +		sci_remote_device_terminate_requests(idev); + +		/* Fall through into the default case... */ +	default: +		clear_bit(IDEV_IO_READY, &idev->flags); +		break; +	} +} + +/* called once the remote node context is ready to be freed. + * The remote device can now report that its stop operation is complete. none + */ +static void rnc_destruct_done(void *_dev) +{ +	struct isci_remote_device *idev = _dev; + +	BUG_ON(idev->started_request_count != 0); +	sci_change_state(&idev->sm, SCI_DEV_STOPPED); +} + +enum sci_status sci_remote_device_terminate_requests( +	struct isci_remote_device *idev) +{ +	return sci_remote_device_terminate_reqs_checkabort(idev, 0); +} +  enum sci_status sci_remote_device_stop(struct isci_remote_device *idev,  					u32 timeout)  { @@ -201,13 +369,16 @@ enum sci_status sci_remote_device_stop(struct isci_remote_device *idev,  	case SCI_SMP_DEV_IDLE:  	case SCI_SMP_DEV_CMD:  		sci_change_state(sm, SCI_DEV_STOPPING); -		if (idev->started_request_count == 0) { +		if (idev->started_request_count == 0)  			sci_remote_node_context_destruct(&idev->rnc, -							      rnc_destruct_done, idev); -			return SCI_SUCCESS; -		} else -			return sci_remote_device_terminate_requests(idev); -		break; +							 rnc_destruct_done, +							 idev); +		else { +			sci_remote_device_suspend( +				idev, SCI_SW_SUSPEND_LINKHANG_DETECT); +			sci_remote_device_terminate_requests(idev); +		} +		return SCI_SUCCESS;  	case SCI_DEV_STOPPING:  		/* All requests should have been terminated, but if there is an  		 * attempt to stop a device already in the stopping state, then @@ -265,22 +436,6 @@ enum sci_status sci_remote_device_reset_complete(struct isci_remote_device *idev  	return SCI_SUCCESS;  } -enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev, -					       u32 suspend_type) -{ -	struct sci_base_state_machine *sm = &idev->sm; -	enum sci_remote_device_states state = sm->current_state_id; - -	if (state != SCI_STP_DEV_CMD) { -		dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n", -			 __func__, dev_state_name(state)); -		return SCI_FAILURE_INVALID_STATE; -	} - -	return sci_remote_node_context_suspend(&idev->rnc, -						    suspend_type, NULL, NULL); -} -  enum sci_status sci_remote_device_frame_handler(struct isci_remote_device *idev,  						     u32 frame_index)  { @@ -412,9 +567,9 @@ static void atapi_remote_device_resume_done(void *_dev)  enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev,  						     u32 event_code)  { +	enum sci_status status;  	struct sci_base_state_machine *sm = &idev->sm;  	enum sci_remote_device_states state = sm->current_state_id; -	enum sci_status status;  	switch (scu_get_event_type(event_code)) {  	case SCU_EVENT_TYPE_RNC_OPS_MISC: @@ -427,9 +582,7 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev,  			status = SCI_SUCCESS;  			/* Suspend the associated RNC */ -			sci_remote_node_context_suspend(&idev->rnc, -							      SCI_SOFTWARE_SUSPENSION, -							      NULL, NULL); +			sci_remote_device_suspend(idev, SCI_SW_SUSPEND_NORMAL);  			dev_dbg(scirdev_to_dev(idev),  				"%s: device: %p event code: %x: %s\n", @@ -455,6 +608,10 @@ enum sci_status sci_remote_device_event_handler(struct isci_remote_device *idev,  	if (status != SCI_SUCCESS)  		return status; +	/* Decode device-specific states that may require an RNC resume during +	 * normal operation.  When the abort path is active, these resumes are +	 * managed when the abort path exits. +	 */  	if (state == SCI_STP_DEV_ATAPI_ERROR) {  		/* For ATAPI error state resume the RNC right away. */  		if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX || @@ -743,10 +900,6 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost,  		if (status != SCI_SUCCESS)  			return status; -		status = sci_remote_node_context_start_task(&idev->rnc, ireq); -		if (status != SCI_SUCCESS) -			goto out; -  		status = sci_request_start(ireq);  		if (status != SCI_SUCCESS)  			goto out; @@ -765,11 +918,11 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost,  		 * the correct action when the remote node context is suspended  		 * and later resumed.  		 */ -		sci_remote_node_context_suspend(&idev->rnc, -				SCI_SOFTWARE_SUSPENSION, NULL, NULL); -		sci_remote_node_context_resume(&idev->rnc, -				sci_remote_device_continue_request, -						    idev); +		sci_remote_device_suspend(idev, +					  SCI_SW_SUSPEND_LINKHANG_DETECT); + +		status = sci_remote_node_context_start_task(&idev->rnc, ireq, +				sci_remote_device_continue_request, idev);  	out:  		sci_remote_device_start_request(idev, ireq, status); @@ -783,7 +936,9 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost,  		if (status != SCI_SUCCESS)  			return status; -		status = sci_remote_node_context_start_task(&idev->rnc, ireq); +		/* Resume the RNC as needed: */ +		status = sci_remote_node_context_start_task(&idev->rnc, ireq, +							    NULL, NULL);  		if (status != SCI_SUCCESS)  			break; @@ -892,7 +1047,7 @@ static void isci_remote_device_deconstruct(struct isci_host *ihost, struct isci_  	 * here should go through isci_remote_device_nuke_requests.  	 * If we hit this condition, we will need a way to complete  	 * io requests in process */ -	BUG_ON(!list_empty(&idev->reqs_in_process)); +	BUG_ON(idev->started_request_count > 0);  	sci_remote_device_destruct(idev);  	list_del_init(&idev->node); @@ -954,14 +1109,21 @@ static void sci_remote_device_ready_state_exit(struct sci_base_state_machine *sm  static void sci_remote_device_resetting_state_enter(struct sci_base_state_machine *sm)  {  	struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm); +	struct isci_host *ihost = idev->owning_port->owning_controller; -	sci_remote_node_context_suspend( -		&idev->rnc, SCI_SOFTWARE_SUSPENSION, NULL, NULL); +	dev_dbg(&ihost->pdev->dev, +		"%s: isci_device = %p\n", __func__, idev); + +	sci_remote_device_suspend(idev, SCI_SW_SUSPEND_LINKHANG_DETECT);  }  static void sci_remote_device_resetting_state_exit(struct sci_base_state_machine *sm)  {  	struct isci_remote_device *idev = container_of(sm, typeof(*idev), sm); +	struct isci_host *ihost = idev->owning_port->owning_controller; + +	dev_dbg(&ihost->pdev->dev, +		"%s: isci_device = %p\n", __func__, idev);  	sci_remote_node_context_resume(&idev->rnc, NULL, NULL);  } @@ -1113,33 +1275,20 @@ static enum sci_status sci_remote_device_da_construct(struct isci_port *iport,  {  	enum sci_status status;  	struct sci_port_properties properties; -	struct domain_device *dev = idev->domain_dev;  	sci_remote_device_construct(iport, idev); -	/* -	 * This information is request to determine how many remote node context -	 * entries will be needed to store the remote node. -	 */ -	idev->is_direct_attached = true; -  	sci_port_get_properties(iport, &properties);  	/* Get accurate port width from port's phy mask for a DA device. */  	idev->device_port_width = hweight32(properties.phy_mask);  	status = sci_controller_allocate_remote_node_context(iport->owning_controller, -								  idev, -								  &idev->rnc.remote_node_index); +							     idev, +							     &idev->rnc.remote_node_index);  	if (status != SCI_SUCCESS)  		return status; -	if (dev->dev_type == SAS_END_DEV || dev->dev_type == SATA_DEV || -	    (dev->tproto & SAS_PROTOCOL_STP) || dev_is_expander(dev)) -		/* pass */; -	else -		return SCI_FAILURE_UNSUPPORTED_PROTOCOL; -  	idev->connection_rate = sci_port_get_max_allowed_speed(iport);  	return SCI_SUCCESS; @@ -1171,19 +1320,13 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport,  	if (status != SCI_SUCCESS)  		return status; -	if (dev->dev_type == SAS_END_DEV || dev->dev_type == SATA_DEV || -	    (dev->tproto & SAS_PROTOCOL_STP) || dev_is_expander(dev)) -		/* pass */; -	else -		return SCI_FAILURE_UNSUPPORTED_PROTOCOL; - -	/* -	 * For SAS-2 the physical link rate is actually a logical link +	/* For SAS-2 the physical link rate is actually a logical link  	 * rate that incorporates multiplexing.  The SCU doesn't  	 * incorporate multiplexing and for the purposes of the  	 * connection the logical link rate is that same as the  	 * physical.  Furthermore, the SAS-2 and SAS-1.1 fields overlay -	 * one another, so this code works for both situations. */ +	 * one another, so this code works for both situations. +	 */  	idev->connection_rate = min_t(u16, sci_port_get_max_allowed_speed(iport),  					 dev->linkrate); @@ -1193,6 +1336,105 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport,  	return SCI_SUCCESS;  } +enum sci_status sci_remote_device_resume( +	struct isci_remote_device *idev, +	scics_sds_remote_node_context_callback cb_fn, +	void *cb_p) +{ +	enum sci_status status; + +	status = sci_remote_node_context_resume(&idev->rnc, cb_fn, cb_p); +	if (status != SCI_SUCCESS) +		dev_dbg(scirdev_to_dev(idev), "%s: failed to resume: %d\n", +			__func__, status); +	return status; +} + +static void isci_remote_device_resume_from_abort_complete(void *cbparam) +{ +	struct isci_remote_device *idev = cbparam; +	struct isci_host *ihost = idev->owning_port->owning_controller; +	scics_sds_remote_node_context_callback abort_resume_cb = +		idev->abort_resume_cb; + +	dev_dbg(scirdev_to_dev(idev), "%s: passing-along resume: %p\n", +		__func__, abort_resume_cb); + +	if (abort_resume_cb != NULL) { +		idev->abort_resume_cb = NULL; +		abort_resume_cb(idev->abort_resume_cbparam); +	} +	clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags); +	wake_up(&ihost->eventq); +} + +static bool isci_remote_device_test_resume_done( +	struct isci_host *ihost, +	struct isci_remote_device *idev) +{ +	unsigned long flags; +	bool done; + +	spin_lock_irqsave(&ihost->scic_lock, flags); +	done = !test_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags) +		|| test_bit(IDEV_STOP_PENDING, &idev->flags) +		|| sci_remote_node_context_is_being_destroyed(&idev->rnc); +	spin_unlock_irqrestore(&ihost->scic_lock, flags); + +	return done; +} + +void isci_remote_device_wait_for_resume_from_abort( +	struct isci_host *ihost, +	struct isci_remote_device *idev) +{ +	dev_dbg(&ihost->pdev->dev, "%s: starting resume wait: %p\n", +		 __func__, idev); + +	#define MAX_RESUME_MSECS 10000 +	if (!wait_event_timeout(ihost->eventq, +				isci_remote_device_test_resume_done(ihost, idev), +				msecs_to_jiffies(MAX_RESUME_MSECS))) { + +		dev_warn(&ihost->pdev->dev, "%s: #### Timeout waiting for " +			 "resume: %p\n", __func__, idev); +	} +	clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags); + +	dev_dbg(&ihost->pdev->dev, "%s: resume wait done: %p\n", +		 __func__, idev); +} + +enum sci_status isci_remote_device_resume_from_abort( +	struct isci_host *ihost, +	struct isci_remote_device *idev) +{ +	unsigned long flags; +	enum sci_status status = SCI_SUCCESS; +	int destroyed; + +	spin_lock_irqsave(&ihost->scic_lock, flags); +	/* Preserve any current resume callbacks, for instance from other +	 * resumptions. +	 */ +	idev->abort_resume_cb = idev->rnc.user_callback; +	idev->abort_resume_cbparam = idev->rnc.user_cookie; +	set_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags); +	clear_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags); +	destroyed = sci_remote_node_context_is_being_destroyed(&idev->rnc); +	if (!destroyed) +		status = sci_remote_device_resume( +			idev, isci_remote_device_resume_from_abort_complete, +			idev); +	spin_unlock_irqrestore(&ihost->scic_lock, flags); +	if (!destroyed && (status == SCI_SUCCESS)) +		isci_remote_device_wait_for_resume_from_abort(ihost, idev); +	else +		clear_bit(IDEV_ABORT_PATH_RESUME_PENDING, &idev->flags); + +	return status; +} +  /**   * sci_remote_device_start() - This method will start the supplied remote   *    device.  This method enables normal IO requests to flow through to the @@ -1207,7 +1449,7 @@ static enum sci_status sci_remote_device_ea_construct(struct isci_port *iport,   * the device when there have been no phys added to it.   */  static enum sci_status sci_remote_device_start(struct isci_remote_device *idev, -						u32 timeout) +					       u32 timeout)  {  	struct sci_base_state_machine *sm = &idev->sm;  	enum sci_remote_device_states state = sm->current_state_id; @@ -1219,9 +1461,8 @@ static enum sci_status sci_remote_device_start(struct isci_remote_device *idev,  		return SCI_FAILURE_INVALID_STATE;  	} -	status = sci_remote_node_context_resume(&idev->rnc, -						     remote_device_resume_done, -						     idev); +	status = sci_remote_device_resume(idev, remote_device_resume_done, +					  idev);  	if (status != SCI_SUCCESS)  		return status; @@ -1259,20 +1500,6 @@ static enum sci_status isci_remote_device_construct(struct isci_port *iport,  	return status;  } -void isci_remote_device_nuke_requests(struct isci_host *ihost, struct isci_remote_device *idev) -{ -	DECLARE_COMPLETION_ONSTACK(aborted_task_completion); - -	dev_dbg(&ihost->pdev->dev, -		"%s: idev = %p\n", __func__, idev); - -	/* Cleanup all requests pending for this device. */ -	isci_terminate_pending_requests(ihost, idev); - -	dev_dbg(&ihost->pdev->dev, -		"%s: idev = %p, done\n", __func__, idev); -} -  /**   * This function builds the isci_remote_device when a libsas dev_found message   *    is received. @@ -1297,10 +1524,6 @@ isci_remote_device_alloc(struct isci_host *ihost, struct isci_port *iport)  		dev_warn(&ihost->pdev->dev, "%s: failed\n", __func__);  		return NULL;  	} - -	if (WARN_ONCE(!list_empty(&idev->reqs_in_process), "found requests in process\n")) -		return NULL; -  	if (WARN_ONCE(!list_empty(&idev->node), "found non-idle remote device\n"))  		return NULL; @@ -1342,14 +1565,8 @@ enum sci_status isci_remote_device_stop(struct isci_host *ihost, struct isci_rem  	spin_lock_irqsave(&ihost->scic_lock, flags);  	idev->domain_dev->lldd_dev = NULL; /* disable new lookups */  	set_bit(IDEV_GONE, &idev->flags); -	spin_unlock_irqrestore(&ihost->scic_lock, flags); - -	/* Kill all outstanding requests. */ -	isci_remote_device_nuke_requests(ihost, idev);  	set_bit(IDEV_STOP_PENDING, &idev->flags); - -	spin_lock_irqsave(&ihost->scic_lock, flags);  	status = sci_remote_device_stop(idev, 50);  	spin_unlock_irqrestore(&ihost->scic_lock, flags); @@ -1359,6 +1576,9 @@ enum sci_status isci_remote_device_stop(struct isci_host *ihost, struct isci_rem  	else  		wait_for_device_stop(ihost, idev); +	dev_dbg(&ihost->pdev->dev, +		"%s: isci_device = %p, waiting done.\n", __func__, idev); +  	return status;  } @@ -1434,3 +1654,73 @@ int isci_remote_device_found(struct domain_device *dev)  	return status == SCI_SUCCESS ? 0 : -ENODEV;  } + +enum sci_status isci_remote_device_suspend_terminate( +	struct isci_host *ihost, +	struct isci_remote_device *idev, +	struct isci_request *ireq) +{ +	unsigned long flags; +	enum sci_status status; + +	/* Put the device into suspension. */ +	spin_lock_irqsave(&ihost->scic_lock, flags); +	set_bit(IDEV_ABORT_PATH_ACTIVE, &idev->flags); +	sci_remote_device_suspend(idev, SCI_SW_SUSPEND_LINKHANG_DETECT); +	spin_unlock_irqrestore(&ihost->scic_lock, flags); + +	/* Terminate and wait for the completions. */ +	status = isci_remote_device_terminate_requests(ihost, idev, ireq); +	if (status != SCI_SUCCESS) +		dev_dbg(&ihost->pdev->dev, +			"%s: isci_remote_device_terminate_requests(%p) " +				"returned %d!\n", +			__func__, idev, status); + +	/* NOTE: RNC resumption is left to the caller! */ +	return status; +} + +int isci_remote_device_is_safe_to_abort( +	struct isci_remote_device *idev) +{ +	return sci_remote_node_context_is_safe_to_abort(&idev->rnc); +} + +enum sci_status sci_remote_device_abort_requests_pending_abort( +	struct isci_remote_device *idev) +{ +	return sci_remote_device_terminate_reqs_checkabort(idev, 1); +} + +enum sci_status isci_remote_device_reset_complete( +	struct isci_host *ihost, +	struct isci_remote_device *idev) +{ +	unsigned long flags; +	enum sci_status status; + +	spin_lock_irqsave(&ihost->scic_lock, flags); +	status = sci_remote_device_reset_complete(idev); +	spin_unlock_irqrestore(&ihost->scic_lock, flags); + +	return status; +} + +void isci_dev_set_hang_detection_timeout( +	struct isci_remote_device *idev, +	u32 timeout) +{ +	if (dev_is_sata(idev->domain_dev)) { +		if (timeout) { +			if (test_and_set_bit(IDEV_RNC_LLHANG_ENABLED, +					     &idev->flags)) +				return;  /* Already enabled. */ +		} else if (!test_and_clear_bit(IDEV_RNC_LLHANG_ENABLED, +					       &idev->flags)) +			return;  /* Not enabled. */ + +		sci_port_set_hang_detection_timeout(idev->owning_port, +						    timeout); +	} +}  |