|
@@ -1331,226 +1331,10 @@ isci_task_request_complete(struct isci_host *ihost,
|
|
|
complete(tmf_complete);
|
|
|
}
|
|
|
|
|
|
-static void isci_smp_task_timedout(unsigned long _task)
|
|
|
-{
|
|
|
- struct sas_task *task = (void *) _task;
|
|
|
- unsigned long flags;
|
|
|
-
|
|
|
- spin_lock_irqsave(&task->task_state_lock, flags);
|
|
|
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
|
|
|
- task->task_state_flags |= SAS_TASK_STATE_ABORTED;
|
|
|
- spin_unlock_irqrestore(&task->task_state_lock, flags);
|
|
|
-
|
|
|
- complete(&task->completion);
|
|
|
-}
|
|
|
-
|
|
|
-static void isci_smp_task_done(struct sas_task *task)
|
|
|
-{
|
|
|
- if (!del_timer(&task->timer))
|
|
|
- return;
|
|
|
- complete(&task->completion);
|
|
|
-}
|
|
|
-
|
|
|
-static int isci_smp_execute_task(struct isci_host *ihost,
|
|
|
- struct domain_device *dev, void *req,
|
|
|
- int req_size, void *resp, int resp_size)
|
|
|
-{
|
|
|
- int res, retry;
|
|
|
- struct sas_task *task = NULL;
|
|
|
-
|
|
|
- for (retry = 0; retry < 3; retry++) {
|
|
|
- task = sas_alloc_task(GFP_KERNEL);
|
|
|
- if (!task)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- task->dev = dev;
|
|
|
- task->task_proto = dev->tproto;
|
|
|
- sg_init_one(&task->smp_task.smp_req, req, req_size);
|
|
|
- sg_init_one(&task->smp_task.smp_resp, resp, resp_size);
|
|
|
-
|
|
|
- task->task_done = isci_smp_task_done;
|
|
|
-
|
|
|
- task->timer.data = (unsigned long) task;
|
|
|
- task->timer.function = isci_smp_task_timedout;
|
|
|
- task->timer.expires = jiffies + 10*HZ;
|
|
|
- add_timer(&task->timer);
|
|
|
-
|
|
|
- res = isci_task_execute_task(task, 1, GFP_KERNEL);
|
|
|
-
|
|
|
- if (res) {
|
|
|
- del_timer(&task->timer);
|
|
|
- dev_dbg(&ihost->pdev->dev,
|
|
|
- "%s: executing SMP task failed:%d\n",
|
|
|
- __func__, res);
|
|
|
- goto ex_err;
|
|
|
- }
|
|
|
-
|
|
|
- wait_for_completion(&task->completion);
|
|
|
- res = -ECOMM;
|
|
|
- if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
|
|
|
- dev_dbg(&ihost->pdev->dev,
|
|
|
- "%s: smp task timed out or aborted\n",
|
|
|
- __func__);
|
|
|
- isci_task_abort_task(task);
|
|
|
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
|
|
|
- dev_dbg(&ihost->pdev->dev,
|
|
|
- "%s: SMP task aborted and not done\n",
|
|
|
- __func__);
|
|
|
- goto ex_err;
|
|
|
- }
|
|
|
- }
|
|
|
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
|
|
|
- task->task_status.stat == SAM_STAT_GOOD) {
|
|
|
- res = 0;
|
|
|
- break;
|
|
|
- }
|
|
|
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
|
|
|
- task->task_status.stat == SAS_DATA_UNDERRUN) {
|
|
|
- /* no error, but return the number of bytes of
|
|
|
- * underrun */
|
|
|
- res = task->task_status.residual;
|
|
|
- break;
|
|
|
- }
|
|
|
- if (task->task_status.resp == SAS_TASK_COMPLETE &&
|
|
|
- task->task_status.stat == SAS_DATA_OVERRUN) {
|
|
|
- res = -EMSGSIZE;
|
|
|
- break;
|
|
|
- } else {
|
|
|
- dev_dbg(&ihost->pdev->dev,
|
|
|
- "%s: task to dev %016llx response: 0x%x "
|
|
|
- "status 0x%x\n", __func__,
|
|
|
- SAS_ADDR(dev->sas_addr),
|
|
|
- task->task_status.resp,
|
|
|
- task->task_status.stat);
|
|
|
- sas_free_task(task);
|
|
|
- task = NULL;
|
|
|
- }
|
|
|
- }
|
|
|
-ex_err:
|
|
|
- BUG_ON(retry == 3 && task != NULL);
|
|
|
- sas_free_task(task);
|
|
|
- return res;
|
|
|
-}
|
|
|
-
|
|
|
-#define DISCOVER_REQ_SIZE 16
|
|
|
-#define DISCOVER_RESP_SIZE 56
|
|
|
-
|
|
|
-int isci_smp_get_phy_attached_dev_type(struct isci_host *ihost,
|
|
|
- struct domain_device *dev,
|
|
|
- int phy_id, int *adt)
|
|
|
-{
|
|
|
- struct smp_resp *disc_resp;
|
|
|
- u8 *disc_req;
|
|
|
- int res;
|
|
|
-
|
|
|
- disc_resp = kzalloc(DISCOVER_RESP_SIZE, GFP_KERNEL);
|
|
|
- if (!disc_resp)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- disc_req = kzalloc(DISCOVER_REQ_SIZE, GFP_KERNEL);
|
|
|
- if (disc_req) {
|
|
|
- disc_req[0] = SMP_REQUEST;
|
|
|
- disc_req[1] = SMP_DISCOVER;
|
|
|
- disc_req[9] = phy_id;
|
|
|
- } else {
|
|
|
- kfree(disc_resp);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
- res = isci_smp_execute_task(ihost, dev, disc_req, DISCOVER_REQ_SIZE,
|
|
|
- disc_resp, DISCOVER_RESP_SIZE);
|
|
|
- if (!res) {
|
|
|
- if (disc_resp->result != SMP_RESP_FUNC_ACC)
|
|
|
- res = disc_resp->result;
|
|
|
- else
|
|
|
- *adt = disc_resp->disc.attached_dev_type;
|
|
|
- }
|
|
|
- kfree(disc_req);
|
|
|
- kfree(disc_resp);
|
|
|
-
|
|
|
- return res;
|
|
|
-}
|
|
|
-
|
|
|
-static void isci_wait_for_smp_phy_reset(struct isci_remote_device *idev, int phy_num)
|
|
|
-{
|
|
|
- struct domain_device *dev = idev->domain_dev;
|
|
|
- struct isci_port *iport = idev->isci_port;
|
|
|
- struct isci_host *ihost = iport->isci_host;
|
|
|
- int res, iteration = 0, attached_device_type;
|
|
|
- #define STP_WAIT_MSECS 25000
|
|
|
- unsigned long tmo = msecs_to_jiffies(STP_WAIT_MSECS);
|
|
|
- unsigned long deadline = jiffies + tmo;
|
|
|
- enum {
|
|
|
- SMP_PHYWAIT_PHYDOWN,
|
|
|
- SMP_PHYWAIT_PHYUP,
|
|
|
- SMP_PHYWAIT_DONE
|
|
|
- } phy_state = SMP_PHYWAIT_PHYDOWN;
|
|
|
-
|
|
|
- /* While there is time, wait for the phy to go away and come back */
|
|
|
- while (time_is_after_jiffies(deadline) && phy_state != SMP_PHYWAIT_DONE) {
|
|
|
- int event = atomic_read(&iport->event);
|
|
|
-
|
|
|
- ++iteration;
|
|
|
-
|
|
|
- tmo = wait_event_timeout(ihost->eventq,
|
|
|
- event != atomic_read(&iport->event) ||
|
|
|
- !test_bit(IPORT_BCN_BLOCKED, &iport->flags),
|
|
|
- tmo);
|
|
|
- /* link down, stop polling */
|
|
|
- if (!test_bit(IPORT_BCN_BLOCKED, &iport->flags))
|
|
|
- break;
|
|
|
-
|
|
|
- dev_dbg(&ihost->pdev->dev,
|
|
|
- "%s: iport %p, iteration %d,"
|
|
|
- " phase %d: time_remaining %lu, bcns = %d\n",
|
|
|
- __func__, iport, iteration, phy_state,
|
|
|
- tmo, test_bit(IPORT_BCN_PENDING, &iport->flags));
|
|
|
-
|
|
|
- res = isci_smp_get_phy_attached_dev_type(ihost, dev, phy_num,
|
|
|
- &attached_device_type);
|
|
|
- tmo = deadline - jiffies;
|
|
|
-
|
|
|
- if (res) {
|
|
|
- dev_dbg(&ihost->pdev->dev,
|
|
|
- "%s: iteration %d, phase %d:"
|
|
|
- " SMP error=%d, time_remaining=%lu\n",
|
|
|
- __func__, iteration, phy_state, res, tmo);
|
|
|
- break;
|
|
|
- }
|
|
|
- dev_dbg(&ihost->pdev->dev,
|
|
|
- "%s: iport %p, iteration %d,"
|
|
|
- " phase %d: time_remaining %lu, bcns = %d, "
|
|
|
- "attdevtype = %x\n",
|
|
|
- __func__, iport, iteration, phy_state,
|
|
|
- tmo, test_bit(IPORT_BCN_PENDING, &iport->flags),
|
|
|
- attached_device_type);
|
|
|
-
|
|
|
- switch (phy_state) {
|
|
|
- case SMP_PHYWAIT_PHYDOWN:
|
|
|
- /* Has the device gone away? */
|
|
|
- if (!attached_device_type)
|
|
|
- phy_state = SMP_PHYWAIT_PHYUP;
|
|
|
-
|
|
|
- break;
|
|
|
-
|
|
|
- case SMP_PHYWAIT_PHYUP:
|
|
|
- /* Has the device come back? */
|
|
|
- if (attached_device_type)
|
|
|
- phy_state = SMP_PHYWAIT_DONE;
|
|
|
- break;
|
|
|
-
|
|
|
- case SMP_PHYWAIT_DONE:
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- dev_dbg(&ihost->pdev->dev, "%s: done\n", __func__);
|
|
|
-}
|
|
|
-
|
|
|
static int isci_reset_device(struct isci_host *ihost,
|
|
|
struct isci_remote_device *idev)
|
|
|
{
|
|
|
struct sas_phy *phy = sas_find_local_phy(idev->domain_dev);
|
|
|
- struct isci_port *iport = idev->isci_port;
|
|
|
enum sci_status status;
|
|
|
unsigned long flags;
|
|
|
int rc;
|
|
@@ -1570,10 +1354,6 @@ static int isci_reset_device(struct isci_host *ihost,
|
|
|
}
|
|
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
|
|
|
|
- /* If this is a device on an expander, disable BCN processing. */
|
|
|
- if (!scsi_is_sas_phy_local(phy))
|
|
|
- set_bit(IPORT_BCN_BLOCKED, &iport->flags);
|
|
|
-
|
|
|
rc = sas_phy_reset(phy, true);
|
|
|
|
|
|
/* Terminate in-progress I/O now. */
|
|
@@ -1584,21 +1364,6 @@ static int isci_reset_device(struct isci_host *ihost,
|
|
|
status = sci_remote_device_reset_complete(idev);
|
|
|
spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
|
|
|
|
- /* If this is a device on an expander, bring the phy back up. */
|
|
|
- if (!scsi_is_sas_phy_local(phy)) {
|
|
|
- /* A phy reset will cause the device to go away then reappear.
|
|
|
- * Since libsas will take action on incoming BCNs (eg. remove
|
|
|
- * a device going through an SMP phy-control driven reset),
|
|
|
- * we need to wait until the phy comes back up before letting
|
|
|
- * discovery proceed in libsas.
|
|
|
- */
|
|
|
- isci_wait_for_smp_phy_reset(idev, phy->number);
|
|
|
-
|
|
|
- spin_lock_irqsave(&ihost->scic_lock, flags);
|
|
|
- isci_port_bcn_enable(ihost, idev->isci_port);
|
|
|
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
|
|
|
- }
|
|
|
-
|
|
|
if (status != SCI_SUCCESS) {
|
|
|
dev_dbg(&ihost->pdev->dev,
|
|
|
"%s: sci_remote_device_reset_complete(%p) "
|