Skip to content

Commit

Permalink
isci: Check IDEV_GONE before performing abort path operations.
Browse files Browse the repository at this point in the history
In the link fail path, set IDEV_GONE for every device on the domain
when the last link in the port fails.

In the abort path functions like isci_reset_device, make sure that
there has not already been a detected domain failure with the device
by checking IDEV_GONE, before performing any kind of hard reset, SMP
phy control, or TMF operation.

The check for IDEV_GONE makes sure that the device in the abort path
really has control of the port with which it is associated.  This
prevents starting hard resets at incorrect times and scheduling
unnecessary LUN resets for SATA devices.

Signed-off-by: Jeff Skirvin <jeffrey.d.skirvin@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
  • Loading branch information
Jeff Skirvin authored and Dan Williams committed May 17, 2012
1 parent 8780516 commit 397497d
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 21 deletions.
23 changes: 23 additions & 0 deletions drivers/scsi/isci/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,32 @@ static void isci_port_link_down(struct isci_host *isci_host,
struct isci_phy *isci_phy,
struct isci_port *isci_port)
{
struct isci_remote_device *isci_device;

dev_dbg(&isci_host->pdev->dev,
"%s: isci_port = %p\n", __func__, isci_port);

if (isci_port) {

/* check to see if this is the last phy on this port. */
if (isci_phy->sas_phy.port &&
isci_phy->sas_phy.port->num_phys == 1) {
/* change the state for all devices on this port. The
* next task sent to this device will be returned as
* SAS_TASK_UNDELIVERED, and the scsi mid layer will
* remove the target
*/
list_for_each_entry(isci_device,
&isci_port->remote_dev_list,
node) {
dev_dbg(&isci_host->pdev->dev,
"%s: isci_device = %p\n",
__func__, isci_device);
set_bit(IDEV_GONE, &isci_device->flags);
}
}
}

/* Notify libsas of the borken link, this will trigger calls to our
* isci_port_deformed and isci_dev_gone functions.
*/
Expand Down
55 changes: 34 additions & 21 deletions drivers/scsi/isci/task.c
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ int isci_task_lu_reset(struct domain_device *dev, u8 *lun)
struct isci_host *ihost = dev_to_ihost(dev);
struct isci_remote_device *idev;
unsigned long flags;
int ret;
int ret = TMF_RESP_FUNC_COMPLETE;

spin_lock_irqsave(&ihost->scic_lock, flags);
idev = isci_get_device(dev->lldd_dev);
Expand All @@ -447,12 +447,12 @@ int isci_task_lu_reset(struct domain_device *dev, u8 *lun)
goto out;
}
/* All pending I/Os have been terminated and cleaned up. */
if (dev_is_sata(dev)) {
sas_ata_schedule_reset(dev);
ret = TMF_RESP_FUNC_COMPLETE;
} else {
/* Send the task management part of the reset. */
ret = isci_task_send_lu_reset_sas(ihost, idev, lun);
if (!test_bit(IDEV_GONE, &idev->flags)) {
if (dev_is_sata(dev))
sas_ata_schedule_reset(dev);
else
/* Send the task management part of the reset. */
ret = isci_task_send_lu_reset_sas(ihost, idev, lun);
}
out:
isci_put_device(idev);
Expand Down Expand Up @@ -512,8 +512,17 @@ int isci_task_abort_task(struct sas_task *task)
spin_unlock_irqrestore(&ihost->scic_lock, flags);

dev_warn(&ihost->pdev->dev,
"%s: dev = %p, task = %p, old_request == %p\n",
__func__, idev, task, old_request);
"%s: dev = %p (%s%s), task = %p, old_request == %p\n",
__func__, idev,
(dev_is_sata(task->dev) ? "STP/SATA"
: ((dev_is_expander(task->dev))
? "SMP"
: "SSP")),
((idev) ? ((test_bit(IDEV_GONE, &idev->flags))
? " IDEV_GONE"
: "")
: " <NULL>"),
task, old_request);

/* Device reset conditions signalled in task_state_flags are the
* responsbility of libsas to observe at the start of the error
Expand Down Expand Up @@ -552,7 +561,8 @@ int isci_task_abort_task(struct sas_task *task)

if (task->task_proto == SAS_PROTOCOL_SMP ||
sas_protocol_ata(task->task_proto) ||
test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags)) {
test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags) ||
test_bit(IDEV_GONE, &idev->flags)) {

spin_unlock_irqrestore(&ihost->scic_lock, flags);

Expand All @@ -561,7 +571,8 @@ int isci_task_abort_task(struct sas_task *task)

dev_warn(&ihost->pdev->dev,
"%s: %s request"
" or complete_in_target (%d), thus no TMF\n",
" or complete_in_target (%d), "
"or IDEV_GONE (%d), thus no TMF\n",
__func__,
((task->task_proto == SAS_PROTOCOL_SMP)
? "SMP"
Expand All @@ -570,7 +581,8 @@ int isci_task_abort_task(struct sas_task *task)
: "<other>")
),
test_bit(IREQ_COMPLETE_IN_TARGET,
&old_request->flags));
&old_request->flags),
test_bit(IDEV_GONE, &idev->flags));

spin_lock_irqsave(&task->task_state_lock, flags);
task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR |
Expand Down Expand Up @@ -734,7 +746,7 @@ static int isci_reset_device(struct isci_host *ihost,
struct domain_device *dev,
struct isci_remote_device *idev)
{
int rc = TMF_RESP_FUNC_COMPLETE, reset_stat;
int rc = TMF_RESP_FUNC_COMPLETE, reset_stat = -1;
struct sas_phy *phy = sas_get_local_phy(dev);
struct isci_port *iport = dev->port->lldd_port;

Expand All @@ -752,14 +764,15 @@ static int isci_reset_device(struct isci_host *ihost,
* primary duty of this function is to cleanup tasks, so that is the
* relevant status.
*/

if (scsi_is_sas_phy_local(phy)) {
struct isci_phy *iphy = &ihost->phys[phy->number];

reset_stat = isci_port_perform_hard_reset(ihost, iport, iphy);
} else
reset_stat = sas_phy_reset(phy, !dev_is_sata(dev));

if (!test_bit(IDEV_GONE, &idev->flags)) {
if (scsi_is_sas_phy_local(phy)) {
struct isci_phy *iphy = &ihost->phys[phy->number];

reset_stat = isci_port_perform_hard_reset(ihost, iport,
iphy);
} else
reset_stat = sas_phy_reset(phy, !dev_is_sata(dev));
}
/* Explicitly resume the RNC here, since there was no task sent. */
isci_remote_device_resume_from_abort(ihost, idev);

Expand Down

0 comments on commit 397497d

Please sign in to comment.