int sas_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
{ struct sas_internal *i = to_sas_internal(host->transportt); struct domain_device *dev = cmd_to_domain_dev(cmd); struct sas_task *task; int res = 0;
/* If the device fell off, no sense in issuing commands */ if (test_bit(SAS_DEV_GONE, &dev->state)) {
cmd->result = DID_BAD_TARGET << 16; goto out_done;
}
if (dev_is_sata(dev)) {
spin_lock_irq(dev->sata_dev.ap->lock);
res = ata_sas_queuecmd(cmd, dev->sata_dev.ap);
spin_unlock_irq(dev->sata_dev.ap->lock); return res;
}
task = sas_create_task(cmd, dev, GFP_ATOMIC); if (!task) return SCSI_MLQUEUE_HOST_BUSY;
res = i->dft->lldd_execute_task(task, GFP_ATOMIC); if (res) goto out_free_task; return 0;
/* At this point, we only get called following an actual abort * of the task, so we should be guaranteed not to be racing with * any completions from the LLD. Task is freed after this.
*/
sas_end_task(cmd, task);
if (dev_is_sata(dev)) { /* defer commands to libata so that libata EH can * handle ata qcs correctly
*/
list_move_tail(&cmd->eh_entry, &sas_ha->eh_ata_q); return;
}
/* now finish the command and move it on to the error * handler done list, this also takes it off the * error handler pending list.
*/
scsi_eh_finish_cmd(cmd, &sas_ha->eh_done_q);
}
if (res == TMF_RESP_FUNC_COMPLETE) {
pr_notice("%s: task 0x%p is aborted\n",
__func__, task); return TASK_IS_ABORTED;
} elseif (si->dft->lldd_query_task) {
pr_notice("%s: querying task 0x%p\n", __func__, task);
res = si->dft->lldd_query_task(task); switch (res) { case TMF_RESP_FUNC_SUCC:
pr_notice("%s: task 0x%p at LU\n", __func__,
task); return TASK_IS_AT_LU; case TMF_RESP_FUNC_COMPLETE:
pr_notice("%s: task 0x%p not at LU\n",
__func__, task); return TASK_IS_NOT_AT_LU; case TMF_RESP_FUNC_FAILED:
pr_notice("%s: task 0x%p failed to abort\n",
__func__, task); return TASK_ABORT_FAILED; default:
pr_notice("%s: task 0x%p result code %d not handled\n",
__func__, task, res);
}
}
} return TASK_ABORT_FAILED;
}
staticint sas_recover_lu(struct domain_device *dev, struct scsi_cmnd *cmd)
{ int res = TMF_RESP_FUNC_FAILED; struct scsi_lun lun; struct sas_internal *i =
to_sas_internal(dev->port->ha->shost->transportt);
int_to_scsilun(cmd->device->lun, &lun);
pr_notice("eh: device %016llx LUN 0x%llx has the task\n",
SAS_ADDR(dev->sas_addr),
cmd->device->lun);
if (i->dft->lldd_abort_task_set)
res = i->dft->lldd_abort_task_set(dev, lun.scsi_lun);
if (res == TMF_RESP_FUNC_FAILED) { if (i->dft->lldd_clear_task_set)
res = i->dft->lldd_clear_task_set(dev, lun.scsi_lun);
}
if (res == TMF_RESP_FUNC_FAILED) { if (i->dft->lldd_lu_reset)
res = i->dft->lldd_lu_reset(dev, lun.scsi_lun);
}
return res;
}
staticint sas_recover_I_T(struct domain_device *dev)
{ int res = TMF_RESP_FUNC_FAILED; struct sas_internal *i =
to_sas_internal(dev->port->ha->shost->transportt);
pr_notice("I_T nexus reset for dev %016llx\n",
SAS_ADDR(dev->sas_addr));
if (i->dft->lldd_I_T_nexus_reset)
res = i->dft->lldd_I_T_nexus_reset(dev);
return res;
}
/* take a reference on the last known good phy for this device */ struct sas_phy *sas_get_local_phy(struct domain_device *dev)
{ struct sas_ha_struct *ha = dev->port->ha; struct sas_phy *phy; unsignedlong flags;
/* a published domain device always has a valid phy, it may be * stale, but it is never NULL
*/
BUG_ON(!dev->phy);
spin_lock_irqsave(host->host_lock, flags); /* We cannot do async aborts for SATA devices */ if (dev_is_sata(dev) && !host->host_eh_scheduled) {
spin_unlock_irqrestore(host->host_lock, flags); return FAILED;
}
spin_unlock_irqrestore(host->host_lock, flags);
if (task)
res = i->dft->lldd_abort_task(task); else
pr_notice("no task to abort\n"); if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE) return SUCCESS;
/* clean out any commands that won the completion vs eh race */
list_for_each_entry_safe(cmd, n, work_q, eh_entry) { struct domain_device *dev = cmd_to_domain_dev(cmd); struct sas_task *task;
spin_lock_irqsave(&dev->done_lock, flags); /* by this point the lldd has either observed * SAS_HA_FROZEN and is leaving the task alone, or has * won the race with eh and decided to complete it
*/
task = TO_SAS_TASK(cmd);
spin_unlock_irqrestore(&dev->done_lock, flags);
if (!task)
list_move_tail(&cmd->eh_entry, &done);
}
pr_debug("trying to find task 0x%p\n", task);
res = sas_scsi_find_task(task);
switch (res) { case TASK_IS_DONE:
pr_notice("%s: task 0x%p is done\n", __func__,
task);
sas_eh_finish_cmd(cmd); continue; case TASK_IS_ABORTED:
pr_notice("%s: task 0x%p is aborted\n",
__func__, task);
sas_eh_finish_cmd(cmd); continue; case TASK_IS_AT_LU:
pr_info("task 0x%p is at LU: lu recover\n", task);
reset:
tmf_resp = sas_recover_lu(task->dev, cmd); if (tmf_resp == TMF_RESP_FUNC_COMPLETE) {
pr_notice("dev %016llx LU 0x%llx is recovered\n",
SAS_ADDR(task->dev),
cmd->device->lun);
sas_eh_finish_cmd(cmd);
sas_scsi_clear_queue_lu(work_q, cmd); goto Again;
}
fallthrough; case TASK_IS_NOT_AT_LU: case TASK_ABORT_FAILED:
pr_notice("task 0x%p is not at LU: I_T recover\n",
task);
tmf_resp = sas_recover_I_T(task->dev); if (tmf_resp == TMF_RESP_FUNC_COMPLETE ||
tmf_resp == -ENODEV) { struct domain_device *dev = task->dev;
pr_notice("I_T %016llx recovered\n",
SAS_ADDR(task->dev->sas_addr));
sas_eh_finish_cmd(cmd);
sas_scsi_clear_queue_I_T(work_q, dev); goto Again;
} /* Hammer time :-) */
try_to_reset_cmd_device(cmd); if (i->dft->lldd_clear_nexus_port) { struct asd_sas_port *port = task->dev->port;
pr_debug("clearing nexus for port:%d\n",
port->id);
res = i->dft->lldd_clear_nexus_port(port); if (res == TMF_RESP_FUNC_COMPLETE) {
pr_notice("clear nexus port:%d succeeded\n",
port->id);
sas_eh_finish_cmd(cmd);
sas_scsi_clear_queue_port(work_q,
port); goto Again;
}
} if (i->dft->lldd_clear_nexus_ha) {
pr_debug("clear nexus ha\n");
res = i->dft->lldd_clear_nexus_ha(ha); if (res == TMF_RESP_FUNC_COMPLETE) {
pr_notice("clear nexus ha succeeded\n");
sas_eh_finish_cmd(cmd); goto clear_q;
}
} /* If we are here -- this means that no amount * of effort could recover from errors. Quite * possibly the HA just disappeared.
*/
pr_err("error from device %016llx, LUN 0x%llx couldn't be recovered in any way\n",
SAS_ADDR(task->dev->sas_addr),
cmd->device->lun);
pr_notice("Enter %s busy: %d failed: %d\n",
__func__, scsi_host_busy(shost), shost->host_failed); /* * Deal with commands that still have SAS tasks (i.e. they didn't * complete via the normal sas_task completion mechanism), * SAS_HA_FROZEN gives eh dominion over all sas_task completion.
*/
set_bit(SAS_HA_FROZEN, &ha->state);
sas_eh_handle_sas_errors(shost, &eh_work_q);
clear_bit(SAS_HA_FROZEN, &ha->state); if (list_empty(&eh_work_q)) goto out;
/* * Now deal with SCSI commands that completed ok but have a an error * code (and hopefully sense data) attached. This is roughly what * scsi_unjam_host does, but we skip scsi_eh_abort_cmds because any * command we see here has no sas_task and is thus unknown to the HA.
*/
sas_ata_eh(shost, &eh_work_q); if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q))
scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q);
out:
sas_eh_handle_resets(shost);
/* now link into libata eh --- if we have any ata devices */
sas_ata_strategy_handler(shost);
scsi_eh_flush_done_q(&ha->eh_done_q);
/* check if any new eh work was scheduled during the last run */
spin_lock_irq(&ha->lock); if (ha->eh_active == 0) {
shost->host_eh_scheduled = 0;
retry = false;
}
spin_unlock_irq(&ha->lock);
if (i->dft->lldd_tmf_exec_complete)
i->dft->lldd_tmf_exec_complete(device);
res = TMF_RESP_FUNC_FAILED;
if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
pr_err("TMF task timeout for %016llx and not done\n",
SAS_ADDR(device->sas_addr)); if (i->dft->lldd_tmf_aborted)
i->dft->lldd_tmf_aborted(task); break;
}
pr_warn("TMF task timeout for %016llx and done\n",
SAS_ADDR(device->sas_addr));
}
if (task->task_status.resp == SAS_TASK_COMPLETE &&
task->task_status.stat == TMF_RESP_FUNC_COMPLETE) {
res = TMF_RESP_FUNC_COMPLETE; break;
}
if (task->task_status.resp == SAS_TASK_COMPLETE &&
task->task_status.stat == TMF_RESP_FUNC_SUCC) {
res = TMF_RESP_FUNC_SUCC; 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
*/
pr_warn("TMF task to dev %016llx resp: 0x%x sts 0x%x underrun\n",
SAS_ADDR(device->sas_addr),
task->task_status.resp,
task->task_status.stat);
res = task->task_status.residual; break;
}
if (task->task_status.resp == SAS_TASK_COMPLETE &&
task->task_status.stat == SAS_OPEN_REJECT) {
pr_warn("TMF task open reject failed %016llx\n",
SAS_ADDR(device->sas_addr));
res = -EIO;
} else {
pr_warn("TMF task to dev %016llx resp: 0x%x status 0x%x\n",
SAS_ADDR(device->sas_addr),
task->task_status.resp,
task->task_status.stat);
}
sas_free_task(task);
task = NULL;
}
if (retry == TASK_RETRY)
pr_warn("executing TMF for %016llx failed after %d attempts!\n",
SAS_ADDR(device->sas_addr), TASK_RETRY);
sas_free_task(task);
/* * Tell an upper layer that it needs to initiate an abort for a given task. * This should only ever be called by an LLDD.
*/ void sas_task_abort(struct sas_task *task)
{ struct scsi_cmnd *sc = task->uldd_task;
/* Escape for libsas internal commands */ if (!sc) { struct sas_task_slow *slow = task->slow_task;
if (!slow) return; if (!timer_delete(&slow->timer)) return;
slow->timer.function(&slow->timer); return;
}
if (dev_is_sata(task->dev))
sas_ata_task_abort(task); else
blk_abort_request(scsi_cmd_to_rq(sc));
}
EXPORT_SYMBOL_GPL(sas_task_abort);
int sas_sdev_init(struct scsi_device *sdev)
{ if (dev_is_sata(sdev_to_domain_dev(sdev)) && sdev->lun) return -ENXIO;
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.