sas_scsi_host.c
来自「linux 内核源代码」· C语言 代码 · 共 1,066 行 · 第 1/2 页
C
1,066 行
/* * Serial Attached SCSI (SAS) class SCSI Host glue. * * Copyright (C) 2005 Adaptec, Inc. All rights reserved. * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> * * This file is licensed under GPLv2. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */#include <linux/kthread.h>#include "sas_internal.h"#include <scsi/scsi_host.h>#include <scsi/scsi_device.h>#include <scsi/scsi_tcq.h>#include <scsi/scsi.h>#include <scsi/scsi_eh.h>#include <scsi/scsi_transport.h>#include <scsi/scsi_transport_sas.h>#include <scsi/sas_ata.h>#include "../scsi_sas_internal.h"#include "../scsi_transport_api.h"#include "../scsi_priv.h"#include <linux/err.h>#include <linux/blkdev.h>#include <linux/freezer.h>#include <linux/scatterlist.h>#include <linux/libata.h>/* ---------- SCSI Host glue ---------- */static void sas_scsi_task_done(struct sas_task *task){ struct task_status_struct *ts = &task->task_status; struct scsi_cmnd *sc = task->uldd_task; struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(sc->device->host); unsigned ts_flags = task->task_state_flags; int hs = 0, stat = 0; if (unlikely(!sc)) { SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n"); list_del_init(&task->list); sas_free_task(task); return; } if (ts->resp == SAS_TASK_UNDELIVERED) { /* transport error */ hs = DID_NO_CONNECT; } else { /* ts->resp == SAS_TASK_COMPLETE */ /* task delivered, what happened afterwards? */ switch (ts->stat) { case SAS_DEV_NO_RESPONSE: case SAS_INTERRUPTED: case SAS_PHY_DOWN: case SAS_NAK_R_ERR: case SAS_OPEN_TO: hs = DID_NO_CONNECT; break; case SAS_DATA_UNDERRUN: scsi_set_resid(sc, ts->residual); if (scsi_bufflen(sc) - scsi_get_resid(sc) < sc->underflow) hs = DID_ERROR; break; case SAS_DATA_OVERRUN: hs = DID_ERROR; break; case SAS_QUEUE_FULL: hs = DID_SOFT_ERROR; /* retry */ break; case SAS_DEVICE_UNKNOWN: hs = DID_BAD_TARGET; break; case SAS_SG_ERR: hs = DID_PARITY; break; case SAS_OPEN_REJECT: if (ts->open_rej_reason == SAS_OREJ_RSVD_RETRY) hs = DID_SOFT_ERROR; /* retry */ else hs = DID_ERROR; break; case SAS_PROTO_RESPONSE: SAS_DPRINTK("LLDD:%s sent SAS_PROTO_RESP for an SSP " "task; please report this\n", task->dev->port->ha->sas_ha_name); break; case SAS_ABORTED_TASK: hs = DID_ABORT; break; case SAM_CHECK_COND: memcpy(sc->sense_buffer, ts->buf, max(SCSI_SENSE_BUFFERSIZE, ts->buf_valid_size)); stat = SAM_CHECK_COND; break; default: stat = ts->stat; break; } } ASSIGN_SAS_TASK(sc, NULL); sc->result = (hs << 16) | stat; list_del_init(&task->list); sas_free_task(task); /* This is very ugly but this is how SCSI Core works. */ if (ts_flags & SAS_TASK_STATE_ABORTED) scsi_eh_finish_cmd(sc, &sas_ha->eh_done_q); else sc->scsi_done(sc);}static enum task_attribute sas_scsi_get_task_attr(struct scsi_cmnd *cmd){ enum task_attribute ta = TASK_ATTR_SIMPLE; if (cmd->request && blk_rq_tagged(cmd->request)) { if (cmd->device->ordered_tags && (cmd->request->cmd_flags & REQ_HARDBARRIER)) ta = TASK_ATTR_ORDERED; } return ta;}static struct sas_task *sas_create_task(struct scsi_cmnd *cmd, struct domain_device *dev, gfp_t gfp_flags){ struct sas_task *task = sas_alloc_task(gfp_flags); struct scsi_lun lun; if (!task) return NULL; *(u32 *)cmd->sense_buffer = 0; task->uldd_task = cmd; ASSIGN_SAS_TASK(cmd, task); task->dev = dev; task->task_proto = task->dev->tproto; /* BUG_ON(!SSP) */ task->ssp_task.retry_count = 1; int_to_scsilun(cmd->device->lun, &lun); memcpy(task->ssp_task.LUN, &lun.scsi_lun, 8); task->ssp_task.task_attr = sas_scsi_get_task_attr(cmd); memcpy(task->ssp_task.cdb, cmd->cmnd, 16); task->scatter = scsi_sglist(cmd); task->num_scatter = scsi_sg_count(cmd); task->total_xfer_len = scsi_bufflen(cmd); task->data_dir = cmd->sc_data_direction; task->task_done = sas_scsi_task_done; return task;}int sas_queue_up(struct sas_task *task){ struct sas_ha_struct *sas_ha = task->dev->port->ha; struct scsi_core *core = &sas_ha->core; unsigned long flags; LIST_HEAD(list); spin_lock_irqsave(&core->task_queue_lock, flags); if (sas_ha->lldd_queue_size < core->task_queue_size + 1) { spin_unlock_irqrestore(&core->task_queue_lock, flags); return -SAS_QUEUE_FULL; } list_add_tail(&task->list, &core->task_queue); core->task_queue_size += 1; spin_unlock_irqrestore(&core->task_queue_lock, flags); wake_up_process(core->queue_thread); return 0;}/** * sas_queuecommand -- Enqueue a command for processing * @parameters: See SCSI Core documentation * * Note: XXX: Remove the host unlock/lock pair when SCSI Core can * call us without holding an IRQ spinlock... */int sas_queuecommand(struct scsi_cmnd *cmd, void (*scsi_done)(struct scsi_cmnd *)){ int res = 0; struct domain_device *dev = cmd_to_domain_dev(cmd); struct Scsi_Host *host = cmd->device->host; struct sas_internal *i = to_sas_internal(host->transportt); spin_unlock_irq(host->host_lock); { struct sas_ha_struct *sas_ha = dev->port->ha; struct sas_task *task; if (dev_is_sata(dev)) { unsigned long flags; spin_lock_irqsave(dev->sata_dev.ap->lock, flags); res = ata_sas_queuecmd(cmd, scsi_done, dev->sata_dev.ap); spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); goto out; } res = -ENOMEM; task = sas_create_task(cmd, dev, GFP_ATOMIC); if (!task) goto out; cmd->scsi_done = scsi_done; /* Queue up, Direct Mode or Task Collector Mode. */ if (sas_ha->lldd_max_execute_num < 2) res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); else res = sas_queue_up(task); /* Examine */ if (res) { SAS_DPRINTK("lldd_execute_task returned: %d\n", res); ASSIGN_SAS_TASK(cmd, NULL); sas_free_task(task); if (res == -SAS_QUEUE_FULL) { cmd->result = DID_SOFT_ERROR << 16; /* retry */ res = 0; scsi_done(cmd); } goto out; } }out: spin_lock_irq(host->host_lock); return res;}static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd *my_cmd){ struct scsi_cmnd *cmd, *n; list_for_each_entry_safe(cmd, n, error_q, eh_entry) { if (cmd == my_cmd) list_del_init(&cmd->eh_entry); }}static void sas_scsi_clear_queue_I_T(struct list_head *error_q, struct domain_device *dev){ struct scsi_cmnd *cmd, *n; list_for_each_entry_safe(cmd, n, error_q, eh_entry) { struct domain_device *x = cmd_to_domain_dev(cmd); if (x == dev) list_del_init(&cmd->eh_entry); }}static void sas_scsi_clear_queue_port(struct list_head *error_q, struct asd_sas_port *port){ struct scsi_cmnd *cmd, *n; list_for_each_entry_safe(cmd, n, error_q, eh_entry) { struct domain_device *dev = cmd_to_domain_dev(cmd); struct asd_sas_port *x = dev->port; if (x == port) list_del_init(&cmd->eh_entry); }}enum task_disposition { TASK_IS_DONE, TASK_IS_ABORTED, TASK_IS_AT_LU, TASK_IS_NOT_AT_LU, TASK_ABORT_FAILED,};static enum task_disposition sas_scsi_find_task(struct sas_task *task){ struct sas_ha_struct *ha = task->dev->port->ha; unsigned long flags; int i, res; struct sas_internal *si = to_sas_internal(task->dev->port->ha->core.shost->transportt); if (ha->lldd_max_execute_num > 1) { struct scsi_core *core = &ha->core; struct sas_task *t, *n; spin_lock_irqsave(&core->task_queue_lock, flags); list_for_each_entry_safe(t, n, &core->task_queue, list) { if (task == t) { list_del_init(&t->list); spin_unlock_irqrestore(&core->task_queue_lock, flags); SAS_DPRINTK("%s: task 0x%p aborted from " "task_queue\n", __FUNCTION__, task); return TASK_IS_ABORTED; } } spin_unlock_irqrestore(&core->task_queue_lock, flags); } for (i = 0; i < 5; i++) { SAS_DPRINTK("%s: aborting task 0x%p\n", __FUNCTION__, task); res = si->dft->lldd_abort_task(task); spin_lock_irqsave(&task->task_state_lock, flags); if (task->task_state_flags & SAS_TASK_STATE_DONE) { spin_unlock_irqrestore(&task->task_state_lock, flags); SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__, task); return TASK_IS_DONE; } spin_unlock_irqrestore(&task->task_state_lock, flags); if (res == TMF_RESP_FUNC_COMPLETE) { SAS_DPRINTK("%s: task 0x%p is aborted\n", __FUNCTION__, task); return TASK_IS_ABORTED; } else if (si->dft->lldd_query_task) { SAS_DPRINTK("%s: querying task 0x%p\n", __FUNCTION__, task); res = si->dft->lldd_query_task(task); switch (res) { case TMF_RESP_FUNC_SUCC: SAS_DPRINTK("%s: task 0x%p at LU\n", __FUNCTION__, task); return TASK_IS_AT_LU; case TMF_RESP_FUNC_COMPLETE: SAS_DPRINTK("%s: task 0x%p not at LU\n", __FUNCTION__, task); return TASK_IS_NOT_AT_LU; case TMF_RESP_FUNC_FAILED: SAS_DPRINTK("%s: task 0x%p failed to abort\n", __FUNCTION__, task); return TASK_ABORT_FAILED; } } } return res;}static int 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->core.shost->transportt); int_to_scsilun(cmd->device->lun, &lun); SAS_DPRINTK("eh: device %llx LUN %x 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;}static int sas_recover_I_T(struct domain_device *dev){ int res = TMF_RESP_FUNC_FAILED; struct sas_internal *i = to_sas_internal(dev->port->ha->core.shost->transportt); SAS_DPRINTK("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;}/* Find the sas_phy that's attached to this device */struct sas_phy *find_local_sas_phy(struct domain_device *dev){ struct domain_device *pdev = dev->parent; struct ex_phy *exphy = NULL; int i; /* Directly attached device */ if (!pdev) return dev->port->phy; /* Otherwise look in the expander */ for (i = 0; i < pdev->ex_dev.num_phys; i++) if (!memcmp(dev->sas_addr, pdev->ex_dev.ex_phy[i].attached_sas_addr, SAS_ADDR_SIZE)) { exphy = &pdev->ex_dev.ex_phy[i]; break; } BUG_ON(!exphy); return exphy->phy;}/* Attempt to send a LUN reset message to a device */int sas_eh_device_reset_handler(struct scsi_cmnd *cmd){ struct domain_device *dev = cmd_to_domain_dev(cmd); struct sas_internal *i = to_sas_internal(dev->port->ha->core.shost->transportt); struct scsi_lun lun; int res; int_to_scsilun(cmd->device->lun, &lun); if (!i->dft->lldd_lu_reset) return FAILED; res = i->dft->lldd_lu_reset(dev, lun.scsi_lun); if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE) return SUCCESS; return FAILED;}/* Attempt to send a phy (bus) reset */int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd){ struct domain_device *dev = cmd_to_domain_dev(cmd); struct sas_phy *phy = find_local_sas_phy(dev); int res; res = sas_phy_reset(phy, 1); if (res) SAS_DPRINTK("Bus reset of %s failed 0x%x\n", phy->dev.kobj.k_name, res); if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE) return SUCCESS; return FAILED;}/* Try to reset a device */static int try_to_reset_cmd_device(struct Scsi_Host *shost, struct scsi_cmnd *cmd){ int res; if (!shost->hostt->eh_device_reset_handler) goto try_bus_reset; res = shost->hostt->eh_device_reset_handler(cmd); if (res == SUCCESS) return res;try_bus_reset: if (shost->hostt->eh_bus_reset_handler) return shost->hostt->eh_bus_reset_handler(cmd); return FAILED;}static int sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q){ struct scsi_cmnd *cmd, *n; enum task_disposition res = TASK_IS_DONE; int tmf_resp, need_reset; struct sas_internal *i = to_sas_internal(shost->transportt); unsigned long flags; struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);Again: list_for_each_entry_safe(cmd, n, work_q, eh_entry) { struct sas_task *task = TO_SAS_TASK(cmd); if (!task) continue; list_del_init(&cmd->eh_entry); spin_lock_irqsave(&task->task_state_lock, flags); need_reset = task->task_state_flags & SAS_TASK_NEED_DEV_RESET; spin_unlock_irqrestore(&task->task_state_lock, flags); SAS_DPRINTK("trying to find task 0x%p\n", task); res = sas_scsi_find_task(task); cmd->eh_eflags = 0; switch (res) { case TASK_IS_DONE: SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__, task); task->task_done(task); if (need_reset) try_to_reset_cmd_device(shost, cmd); continue; case TASK_IS_ABORTED: SAS_DPRINTK("%s: task 0x%p is aborted\n",
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?