1*2908d778SJames Bottomley /* 2*2908d778SJames Bottomley * Serial Attached SCSI (SAS) class SCSI Host glue. 3*2908d778SJames Bottomley * 4*2908d778SJames Bottomley * Copyright (C) 2005 Adaptec, Inc. All rights reserved. 5*2908d778SJames Bottomley * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> 6*2908d778SJames Bottomley * 7*2908d778SJames Bottomley * This file is licensed under GPLv2. 8*2908d778SJames Bottomley * 9*2908d778SJames Bottomley * This program is free software; you can redistribute it and/or 10*2908d778SJames Bottomley * modify it under the terms of the GNU General Public License as 11*2908d778SJames Bottomley * published by the Free Software Foundation; either version 2 of the 12*2908d778SJames Bottomley * License, or (at your option) any later version. 13*2908d778SJames Bottomley * 14*2908d778SJames Bottomley * This program is distributed in the hope that it will be useful, but 15*2908d778SJames Bottomley * WITHOUT ANY WARRANTY; without even the implied warranty of 16*2908d778SJames Bottomley * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17*2908d778SJames Bottomley * General Public License for more details. 18*2908d778SJames Bottomley * 19*2908d778SJames Bottomley * You should have received a copy of the GNU General Public License 20*2908d778SJames Bottomley * along with this program; if not, write to the Free Software 21*2908d778SJames Bottomley * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 22*2908d778SJames Bottomley * USA 23*2908d778SJames Bottomley * 24*2908d778SJames Bottomley */ 25*2908d778SJames Bottomley 26*2908d778SJames Bottomley #include "sas_internal.h" 27*2908d778SJames Bottomley 28*2908d778SJames Bottomley #include <scsi/scsi_host.h> 29*2908d778SJames Bottomley #include <scsi/scsi_device.h> 30*2908d778SJames Bottomley #include <scsi/scsi_tcq.h> 31*2908d778SJames Bottomley #include <scsi/scsi.h> 32*2908d778SJames Bottomley #include <scsi/scsi_transport.h> 33*2908d778SJames Bottomley #include <scsi/scsi_transport_sas.h> 34*2908d778SJames Bottomley #include "../scsi_sas_internal.h" 35*2908d778SJames Bottomley 36*2908d778SJames Bottomley #include <linux/err.h> 37*2908d778SJames Bottomley #include <linux/blkdev.h> 38*2908d778SJames Bottomley #include <linux/scatterlist.h> 39*2908d778SJames Bottomley 40*2908d778SJames Bottomley /* ---------- SCSI Host glue ---------- */ 41*2908d778SJames Bottomley 42*2908d778SJames Bottomley #define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble) 43*2908d778SJames Bottomley #define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0) 44*2908d778SJames Bottomley 45*2908d778SJames Bottomley static void sas_scsi_task_done(struct sas_task *task) 46*2908d778SJames Bottomley { 47*2908d778SJames Bottomley struct task_status_struct *ts = &task->task_status; 48*2908d778SJames Bottomley struct scsi_cmnd *sc = task->uldd_task; 49*2908d778SJames Bottomley unsigned ts_flags = task->task_state_flags; 50*2908d778SJames Bottomley int hs = 0, stat = 0; 51*2908d778SJames Bottomley 52*2908d778SJames Bottomley if (unlikely(!sc)) { 53*2908d778SJames Bottomley SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n"); 54*2908d778SJames Bottomley list_del_init(&task->list); 55*2908d778SJames Bottomley sas_free_task(task); 56*2908d778SJames Bottomley return; 57*2908d778SJames Bottomley } 58*2908d778SJames Bottomley 59*2908d778SJames Bottomley if (ts->resp == SAS_TASK_UNDELIVERED) { 60*2908d778SJames Bottomley /* transport error */ 61*2908d778SJames Bottomley hs = DID_NO_CONNECT; 62*2908d778SJames Bottomley } else { /* ts->resp == SAS_TASK_COMPLETE */ 63*2908d778SJames Bottomley /* task delivered, what happened afterwards? */ 64*2908d778SJames Bottomley switch (ts->stat) { 65*2908d778SJames Bottomley case SAS_DEV_NO_RESPONSE: 66*2908d778SJames Bottomley case SAS_INTERRUPTED: 67*2908d778SJames Bottomley case SAS_PHY_DOWN: 68*2908d778SJames Bottomley case SAS_NAK_R_ERR: 69*2908d778SJames Bottomley case SAS_OPEN_TO: 70*2908d778SJames Bottomley hs = DID_NO_CONNECT; 71*2908d778SJames Bottomley break; 72*2908d778SJames Bottomley case SAS_DATA_UNDERRUN: 73*2908d778SJames Bottomley sc->resid = ts->residual; 74*2908d778SJames Bottomley if (sc->request_bufflen - sc->resid < sc->underflow) 75*2908d778SJames Bottomley hs = DID_ERROR; 76*2908d778SJames Bottomley break; 77*2908d778SJames Bottomley case SAS_DATA_OVERRUN: 78*2908d778SJames Bottomley hs = DID_ERROR; 79*2908d778SJames Bottomley break; 80*2908d778SJames Bottomley case SAS_QUEUE_FULL: 81*2908d778SJames Bottomley hs = DID_SOFT_ERROR; /* retry */ 82*2908d778SJames Bottomley break; 83*2908d778SJames Bottomley case SAS_DEVICE_UNKNOWN: 84*2908d778SJames Bottomley hs = DID_BAD_TARGET; 85*2908d778SJames Bottomley break; 86*2908d778SJames Bottomley case SAS_SG_ERR: 87*2908d778SJames Bottomley hs = DID_PARITY; 88*2908d778SJames Bottomley break; 89*2908d778SJames Bottomley case SAS_OPEN_REJECT: 90*2908d778SJames Bottomley if (ts->open_rej_reason == SAS_OREJ_RSVD_RETRY) 91*2908d778SJames Bottomley hs = DID_SOFT_ERROR; /* retry */ 92*2908d778SJames Bottomley else 93*2908d778SJames Bottomley hs = DID_ERROR; 94*2908d778SJames Bottomley break; 95*2908d778SJames Bottomley case SAS_PROTO_RESPONSE: 96*2908d778SJames Bottomley SAS_DPRINTK("LLDD:%s sent SAS_PROTO_RESP for an SSP " 97*2908d778SJames Bottomley "task; please report this\n", 98*2908d778SJames Bottomley task->dev->port->ha->sas_ha_name); 99*2908d778SJames Bottomley break; 100*2908d778SJames Bottomley case SAS_ABORTED_TASK: 101*2908d778SJames Bottomley hs = DID_ABORT; 102*2908d778SJames Bottomley break; 103*2908d778SJames Bottomley case SAM_CHECK_COND: 104*2908d778SJames Bottomley memcpy(sc->sense_buffer, ts->buf, 105*2908d778SJames Bottomley max(SCSI_SENSE_BUFFERSIZE, ts->buf_valid_size)); 106*2908d778SJames Bottomley stat = SAM_CHECK_COND; 107*2908d778SJames Bottomley break; 108*2908d778SJames Bottomley default: 109*2908d778SJames Bottomley stat = ts->stat; 110*2908d778SJames Bottomley break; 111*2908d778SJames Bottomley } 112*2908d778SJames Bottomley } 113*2908d778SJames Bottomley ASSIGN_SAS_TASK(sc, NULL); 114*2908d778SJames Bottomley sc->result = (hs << 16) | stat; 115*2908d778SJames Bottomley list_del_init(&task->list); 116*2908d778SJames Bottomley sas_free_task(task); 117*2908d778SJames Bottomley /* This is very ugly but this is how SCSI Core works. */ 118*2908d778SJames Bottomley if (ts_flags & SAS_TASK_STATE_ABORTED) 119*2908d778SJames Bottomley scsi_finish_command(sc); 120*2908d778SJames Bottomley else 121*2908d778SJames Bottomley sc->scsi_done(sc); 122*2908d778SJames Bottomley } 123*2908d778SJames Bottomley 124*2908d778SJames Bottomley static enum task_attribute sas_scsi_get_task_attr(struct scsi_cmnd *cmd) 125*2908d778SJames Bottomley { 126*2908d778SJames Bottomley enum task_attribute ta = TASK_ATTR_SIMPLE; 127*2908d778SJames Bottomley if (cmd->request && blk_rq_tagged(cmd->request)) { 128*2908d778SJames Bottomley if (cmd->device->ordered_tags && 129*2908d778SJames Bottomley (cmd->request->flags & REQ_HARDBARRIER)) 130*2908d778SJames Bottomley ta = TASK_ATTR_HOQ; 131*2908d778SJames Bottomley } 132*2908d778SJames Bottomley return ta; 133*2908d778SJames Bottomley } 134*2908d778SJames Bottomley 135*2908d778SJames Bottomley static struct sas_task *sas_create_task(struct scsi_cmnd *cmd, 136*2908d778SJames Bottomley struct domain_device *dev, 137*2908d778SJames Bottomley unsigned long gfp_flags) 138*2908d778SJames Bottomley { 139*2908d778SJames Bottomley struct sas_task *task = sas_alloc_task(gfp_flags); 140*2908d778SJames Bottomley struct scsi_lun lun; 141*2908d778SJames Bottomley 142*2908d778SJames Bottomley if (!task) 143*2908d778SJames Bottomley return NULL; 144*2908d778SJames Bottomley 145*2908d778SJames Bottomley *(u32 *)cmd->sense_buffer = 0; 146*2908d778SJames Bottomley task->uldd_task = cmd; 147*2908d778SJames Bottomley ASSIGN_SAS_TASK(cmd, task); 148*2908d778SJames Bottomley 149*2908d778SJames Bottomley task->dev = dev; 150*2908d778SJames Bottomley task->task_proto = task->dev->tproto; /* BUG_ON(!SSP) */ 151*2908d778SJames Bottomley 152*2908d778SJames Bottomley task->ssp_task.retry_count = 1; 153*2908d778SJames Bottomley int_to_scsilun(cmd->device->lun, &lun); 154*2908d778SJames Bottomley memcpy(task->ssp_task.LUN, &lun.scsi_lun, 8); 155*2908d778SJames Bottomley task->ssp_task.task_attr = sas_scsi_get_task_attr(cmd); 156*2908d778SJames Bottomley memcpy(task->ssp_task.cdb, cmd->cmnd, 16); 157*2908d778SJames Bottomley 158*2908d778SJames Bottomley task->scatter = cmd->request_buffer; 159*2908d778SJames Bottomley task->num_scatter = cmd->use_sg; 160*2908d778SJames Bottomley task->total_xfer_len = cmd->request_bufflen; 161*2908d778SJames Bottomley task->data_dir = cmd->sc_data_direction; 162*2908d778SJames Bottomley 163*2908d778SJames Bottomley task->task_done = sas_scsi_task_done; 164*2908d778SJames Bottomley 165*2908d778SJames Bottomley return task; 166*2908d778SJames Bottomley } 167*2908d778SJames Bottomley 168*2908d778SJames Bottomley static int sas_queue_up(struct sas_task *task) 169*2908d778SJames Bottomley { 170*2908d778SJames Bottomley struct sas_ha_struct *sas_ha = task->dev->port->ha; 171*2908d778SJames Bottomley struct scsi_core *core = &sas_ha->core; 172*2908d778SJames Bottomley unsigned long flags; 173*2908d778SJames Bottomley LIST_HEAD(list); 174*2908d778SJames Bottomley 175*2908d778SJames Bottomley spin_lock_irqsave(&core->task_queue_lock, flags); 176*2908d778SJames Bottomley if (sas_ha->lldd_queue_size < core->task_queue_size + 1) { 177*2908d778SJames Bottomley spin_unlock_irqrestore(&core->task_queue_lock, flags); 178*2908d778SJames Bottomley return -SAS_QUEUE_FULL; 179*2908d778SJames Bottomley } 180*2908d778SJames Bottomley list_add_tail(&task->list, &core->task_queue); 181*2908d778SJames Bottomley core->task_queue_size += 1; 182*2908d778SJames Bottomley spin_unlock_irqrestore(&core->task_queue_lock, flags); 183*2908d778SJames Bottomley up(&core->queue_thread_sema); 184*2908d778SJames Bottomley 185*2908d778SJames Bottomley return 0; 186*2908d778SJames Bottomley } 187*2908d778SJames Bottomley 188*2908d778SJames Bottomley /** 189*2908d778SJames Bottomley * sas_queuecommand -- Enqueue a command for processing 190*2908d778SJames Bottomley * @parameters: See SCSI Core documentation 191*2908d778SJames Bottomley * 192*2908d778SJames Bottomley * Note: XXX: Remove the host unlock/lock pair when SCSI Core can 193*2908d778SJames Bottomley * call us without holding an IRQ spinlock... 194*2908d778SJames Bottomley */ 195*2908d778SJames Bottomley int sas_queuecommand(struct scsi_cmnd *cmd, 196*2908d778SJames Bottomley void (*scsi_done)(struct scsi_cmnd *)) 197*2908d778SJames Bottomley { 198*2908d778SJames Bottomley int res = 0; 199*2908d778SJames Bottomley struct domain_device *dev = cmd_to_domain_dev(cmd); 200*2908d778SJames Bottomley struct Scsi_Host *host = cmd->device->host; 201*2908d778SJames Bottomley struct sas_internal *i = to_sas_internal(host->transportt); 202*2908d778SJames Bottomley 203*2908d778SJames Bottomley spin_unlock_irq(host->host_lock); 204*2908d778SJames Bottomley 205*2908d778SJames Bottomley { 206*2908d778SJames Bottomley struct sas_ha_struct *sas_ha = dev->port->ha; 207*2908d778SJames Bottomley struct sas_task *task; 208*2908d778SJames Bottomley 209*2908d778SJames Bottomley res = -ENOMEM; 210*2908d778SJames Bottomley task = sas_create_task(cmd, dev, GFP_ATOMIC); 211*2908d778SJames Bottomley if (!task) 212*2908d778SJames Bottomley goto out; 213*2908d778SJames Bottomley 214*2908d778SJames Bottomley cmd->scsi_done = scsi_done; 215*2908d778SJames Bottomley /* Queue up, Direct Mode or Task Collector Mode. */ 216*2908d778SJames Bottomley if (sas_ha->lldd_max_execute_num < 2) 217*2908d778SJames Bottomley res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); 218*2908d778SJames Bottomley else 219*2908d778SJames Bottomley res = sas_queue_up(task); 220*2908d778SJames Bottomley 221*2908d778SJames Bottomley /* Examine */ 222*2908d778SJames Bottomley if (res) { 223*2908d778SJames Bottomley SAS_DPRINTK("lldd_execute_task returned: %d\n", res); 224*2908d778SJames Bottomley ASSIGN_SAS_TASK(cmd, NULL); 225*2908d778SJames Bottomley sas_free_task(task); 226*2908d778SJames Bottomley if (res == -SAS_QUEUE_FULL) { 227*2908d778SJames Bottomley cmd->result = DID_SOFT_ERROR << 16; /* retry */ 228*2908d778SJames Bottomley res = 0; 229*2908d778SJames Bottomley scsi_done(cmd); 230*2908d778SJames Bottomley } 231*2908d778SJames Bottomley goto out; 232*2908d778SJames Bottomley } 233*2908d778SJames Bottomley } 234*2908d778SJames Bottomley out: 235*2908d778SJames Bottomley spin_lock_irq(host->host_lock); 236*2908d778SJames Bottomley return res; 237*2908d778SJames Bottomley } 238*2908d778SJames Bottomley 239*2908d778SJames Bottomley static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd *my_cmd) 240*2908d778SJames Bottomley { 241*2908d778SJames Bottomley struct scsi_cmnd *cmd, *n; 242*2908d778SJames Bottomley 243*2908d778SJames Bottomley list_for_each_entry_safe(cmd, n, error_q, eh_entry) { 244*2908d778SJames Bottomley if (cmd == my_cmd) 245*2908d778SJames Bottomley list_del_init(&cmd->eh_entry); 246*2908d778SJames Bottomley } 247*2908d778SJames Bottomley } 248*2908d778SJames Bottomley 249*2908d778SJames Bottomley static void sas_scsi_clear_queue_I_T(struct list_head *error_q, 250*2908d778SJames Bottomley struct domain_device *dev) 251*2908d778SJames Bottomley { 252*2908d778SJames Bottomley struct scsi_cmnd *cmd, *n; 253*2908d778SJames Bottomley 254*2908d778SJames Bottomley list_for_each_entry_safe(cmd, n, error_q, eh_entry) { 255*2908d778SJames Bottomley struct domain_device *x = cmd_to_domain_dev(cmd); 256*2908d778SJames Bottomley 257*2908d778SJames Bottomley if (x == dev) 258*2908d778SJames Bottomley list_del_init(&cmd->eh_entry); 259*2908d778SJames Bottomley } 260*2908d778SJames Bottomley } 261*2908d778SJames Bottomley 262*2908d778SJames Bottomley static void sas_scsi_clear_queue_port(struct list_head *error_q, 263*2908d778SJames Bottomley struct asd_sas_port *port) 264*2908d778SJames Bottomley { 265*2908d778SJames Bottomley struct scsi_cmnd *cmd, *n; 266*2908d778SJames Bottomley 267*2908d778SJames Bottomley list_for_each_entry_safe(cmd, n, error_q, eh_entry) { 268*2908d778SJames Bottomley struct domain_device *dev = cmd_to_domain_dev(cmd); 269*2908d778SJames Bottomley struct asd_sas_port *x = dev->port; 270*2908d778SJames Bottomley 271*2908d778SJames Bottomley if (x == port) 272*2908d778SJames Bottomley list_del_init(&cmd->eh_entry); 273*2908d778SJames Bottomley } 274*2908d778SJames Bottomley } 275*2908d778SJames Bottomley 276*2908d778SJames Bottomley enum task_disposition { 277*2908d778SJames Bottomley TASK_IS_DONE, 278*2908d778SJames Bottomley TASK_IS_ABORTED, 279*2908d778SJames Bottomley TASK_IS_AT_LU, 280*2908d778SJames Bottomley TASK_IS_NOT_AT_LU, 281*2908d778SJames Bottomley }; 282*2908d778SJames Bottomley 283*2908d778SJames Bottomley static enum task_disposition sas_scsi_find_task(struct sas_task *task) 284*2908d778SJames Bottomley { 285*2908d778SJames Bottomley struct sas_ha_struct *ha = task->dev->port->ha; 286*2908d778SJames Bottomley unsigned long flags; 287*2908d778SJames Bottomley int i, res; 288*2908d778SJames Bottomley struct sas_internal *si = 289*2908d778SJames Bottomley to_sas_internal(task->dev->port->ha->core.shost->transportt); 290*2908d778SJames Bottomley 291*2908d778SJames Bottomley if (ha->lldd_max_execute_num > 1) { 292*2908d778SJames Bottomley struct scsi_core *core = &ha->core; 293*2908d778SJames Bottomley struct sas_task *t, *n; 294*2908d778SJames Bottomley 295*2908d778SJames Bottomley spin_lock_irqsave(&core->task_queue_lock, flags); 296*2908d778SJames Bottomley list_for_each_entry_safe(t, n, &core->task_queue, list) { 297*2908d778SJames Bottomley if (task == t) { 298*2908d778SJames Bottomley list_del_init(&t->list); 299*2908d778SJames Bottomley spin_unlock_irqrestore(&core->task_queue_lock, 300*2908d778SJames Bottomley flags); 301*2908d778SJames Bottomley SAS_DPRINTK("%s: task 0x%p aborted from " 302*2908d778SJames Bottomley "task_queue\n", 303*2908d778SJames Bottomley __FUNCTION__, task); 304*2908d778SJames Bottomley return TASK_IS_ABORTED; 305*2908d778SJames Bottomley } 306*2908d778SJames Bottomley } 307*2908d778SJames Bottomley spin_unlock_irqrestore(&core->task_queue_lock, flags); 308*2908d778SJames Bottomley } 309*2908d778SJames Bottomley 310*2908d778SJames Bottomley for (i = 0; i < 5; i++) { 311*2908d778SJames Bottomley SAS_DPRINTK("%s: aborting task 0x%p\n", __FUNCTION__, task); 312*2908d778SJames Bottomley res = si->dft->lldd_abort_task(task); 313*2908d778SJames Bottomley 314*2908d778SJames Bottomley spin_lock_irqsave(&task->task_state_lock, flags); 315*2908d778SJames Bottomley if (task->task_state_flags & SAS_TASK_STATE_DONE) { 316*2908d778SJames Bottomley spin_unlock_irqrestore(&task->task_state_lock, flags); 317*2908d778SJames Bottomley SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__, 318*2908d778SJames Bottomley task); 319*2908d778SJames Bottomley return TASK_IS_DONE; 320*2908d778SJames Bottomley } 321*2908d778SJames Bottomley spin_unlock_irqrestore(&task->task_state_lock, flags); 322*2908d778SJames Bottomley 323*2908d778SJames Bottomley if (res == TMF_RESP_FUNC_COMPLETE) { 324*2908d778SJames Bottomley SAS_DPRINTK("%s: task 0x%p is aborted\n", 325*2908d778SJames Bottomley __FUNCTION__, task); 326*2908d778SJames Bottomley return TASK_IS_ABORTED; 327*2908d778SJames Bottomley } else if (si->dft->lldd_query_task) { 328*2908d778SJames Bottomley SAS_DPRINTK("%s: querying task 0x%p\n", 329*2908d778SJames Bottomley __FUNCTION__, task); 330*2908d778SJames Bottomley res = si->dft->lldd_query_task(task); 331*2908d778SJames Bottomley if (res == TMF_RESP_FUNC_SUCC) { 332*2908d778SJames Bottomley SAS_DPRINTK("%s: task 0x%p at LU\n", 333*2908d778SJames Bottomley __FUNCTION__, task); 334*2908d778SJames Bottomley return TASK_IS_AT_LU; 335*2908d778SJames Bottomley } else if (res == TMF_RESP_FUNC_COMPLETE) { 336*2908d778SJames Bottomley SAS_DPRINTK("%s: task 0x%p not at LU\n", 337*2908d778SJames Bottomley __FUNCTION__, task); 338*2908d778SJames Bottomley return TASK_IS_NOT_AT_LU; 339*2908d778SJames Bottomley } 340*2908d778SJames Bottomley } 341*2908d778SJames Bottomley } 342*2908d778SJames Bottomley return res; 343*2908d778SJames Bottomley } 344*2908d778SJames Bottomley 345*2908d778SJames Bottomley static int sas_recover_lu(struct domain_device *dev, struct scsi_cmnd *cmd) 346*2908d778SJames Bottomley { 347*2908d778SJames Bottomley int res = TMF_RESP_FUNC_FAILED; 348*2908d778SJames Bottomley struct scsi_lun lun; 349*2908d778SJames Bottomley struct sas_internal *i = 350*2908d778SJames Bottomley to_sas_internal(dev->port->ha->core.shost->transportt); 351*2908d778SJames Bottomley 352*2908d778SJames Bottomley int_to_scsilun(cmd->device->lun, &lun); 353*2908d778SJames Bottomley 354*2908d778SJames Bottomley SAS_DPRINTK("eh: device %llx LUN %x has the task\n", 355*2908d778SJames Bottomley SAS_ADDR(dev->sas_addr), 356*2908d778SJames Bottomley cmd->device->lun); 357*2908d778SJames Bottomley 358*2908d778SJames Bottomley if (i->dft->lldd_abort_task_set) 359*2908d778SJames Bottomley res = i->dft->lldd_abort_task_set(dev, lun.scsi_lun); 360*2908d778SJames Bottomley 361*2908d778SJames Bottomley if (res == TMF_RESP_FUNC_FAILED) { 362*2908d778SJames Bottomley if (i->dft->lldd_clear_task_set) 363*2908d778SJames Bottomley res = i->dft->lldd_clear_task_set(dev, lun.scsi_lun); 364*2908d778SJames Bottomley } 365*2908d778SJames Bottomley 366*2908d778SJames Bottomley if (res == TMF_RESP_FUNC_FAILED) { 367*2908d778SJames Bottomley if (i->dft->lldd_lu_reset) 368*2908d778SJames Bottomley res = i->dft->lldd_lu_reset(dev, lun.scsi_lun); 369*2908d778SJames Bottomley } 370*2908d778SJames Bottomley 371*2908d778SJames Bottomley return res; 372*2908d778SJames Bottomley } 373*2908d778SJames Bottomley 374*2908d778SJames Bottomley static int sas_recover_I_T(struct domain_device *dev) 375*2908d778SJames Bottomley { 376*2908d778SJames Bottomley int res = TMF_RESP_FUNC_FAILED; 377*2908d778SJames Bottomley struct sas_internal *i = 378*2908d778SJames Bottomley to_sas_internal(dev->port->ha->core.shost->transportt); 379*2908d778SJames Bottomley 380*2908d778SJames Bottomley SAS_DPRINTK("I_T nexus reset for dev %016llx\n", 381*2908d778SJames Bottomley SAS_ADDR(dev->sas_addr)); 382*2908d778SJames Bottomley 383*2908d778SJames Bottomley if (i->dft->lldd_I_T_nexus_reset) 384*2908d778SJames Bottomley res = i->dft->lldd_I_T_nexus_reset(dev); 385*2908d778SJames Bottomley 386*2908d778SJames Bottomley return res; 387*2908d778SJames Bottomley } 388*2908d778SJames Bottomley 389*2908d778SJames Bottomley void sas_scsi_recover_host(struct Scsi_Host *shost) 390*2908d778SJames Bottomley { 391*2908d778SJames Bottomley struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); 392*2908d778SJames Bottomley unsigned long flags; 393*2908d778SJames Bottomley LIST_HEAD(error_q); 394*2908d778SJames Bottomley struct scsi_cmnd *cmd, *n; 395*2908d778SJames Bottomley enum task_disposition res = TASK_IS_DONE; 396*2908d778SJames Bottomley int tmf_resp; 397*2908d778SJames Bottomley struct sas_internal *i = to_sas_internal(shost->transportt); 398*2908d778SJames Bottomley 399*2908d778SJames Bottomley spin_lock_irqsave(shost->host_lock, flags); 400*2908d778SJames Bottomley list_splice_init(&shost->eh_cmd_q, &error_q); 401*2908d778SJames Bottomley spin_unlock_irqrestore(shost->host_lock, flags); 402*2908d778SJames Bottomley 403*2908d778SJames Bottomley SAS_DPRINTK("Enter %s\n", __FUNCTION__); 404*2908d778SJames Bottomley 405*2908d778SJames Bottomley /* All tasks on this list were marked SAS_TASK_STATE_ABORTED 406*2908d778SJames Bottomley * by sas_scsi_timed_out() callback. 407*2908d778SJames Bottomley */ 408*2908d778SJames Bottomley Again: 409*2908d778SJames Bottomley SAS_DPRINTK("going over list...\n"); 410*2908d778SJames Bottomley list_for_each_entry_safe(cmd, n, &error_q, eh_entry) { 411*2908d778SJames Bottomley struct sas_task *task = TO_SAS_TASK(cmd); 412*2908d778SJames Bottomley 413*2908d778SJames Bottomley SAS_DPRINTK("trying to find task 0x%p\n", task); 414*2908d778SJames Bottomley list_del_init(&cmd->eh_entry); 415*2908d778SJames Bottomley res = sas_scsi_find_task(task); 416*2908d778SJames Bottomley 417*2908d778SJames Bottomley cmd->eh_eflags = 0; 418*2908d778SJames Bottomley shost->host_failed--; 419*2908d778SJames Bottomley 420*2908d778SJames Bottomley switch (res) { 421*2908d778SJames Bottomley case TASK_IS_DONE: 422*2908d778SJames Bottomley SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__, 423*2908d778SJames Bottomley task); 424*2908d778SJames Bottomley task->task_done(task); 425*2908d778SJames Bottomley continue; 426*2908d778SJames Bottomley case TASK_IS_ABORTED: 427*2908d778SJames Bottomley SAS_DPRINTK("%s: task 0x%p is aborted\n", 428*2908d778SJames Bottomley __FUNCTION__, task); 429*2908d778SJames Bottomley task->task_done(task); 430*2908d778SJames Bottomley continue; 431*2908d778SJames Bottomley case TASK_IS_AT_LU: 432*2908d778SJames Bottomley SAS_DPRINTK("task 0x%p is at LU: lu recover\n", task); 433*2908d778SJames Bottomley tmf_resp = sas_recover_lu(task->dev, cmd); 434*2908d778SJames Bottomley if (tmf_resp == TMF_RESP_FUNC_COMPLETE) { 435*2908d778SJames Bottomley SAS_DPRINTK("dev %016llx LU %x is " 436*2908d778SJames Bottomley "recovered\n", 437*2908d778SJames Bottomley SAS_ADDR(task->dev), 438*2908d778SJames Bottomley cmd->device->lun); 439*2908d778SJames Bottomley task->task_done(task); 440*2908d778SJames Bottomley sas_scsi_clear_queue_lu(&error_q, cmd); 441*2908d778SJames Bottomley goto Again; 442*2908d778SJames Bottomley } 443*2908d778SJames Bottomley /* fallthrough */ 444*2908d778SJames Bottomley case TASK_IS_NOT_AT_LU: 445*2908d778SJames Bottomley SAS_DPRINTK("task 0x%p is not at LU: I_T recover\n", 446*2908d778SJames Bottomley task); 447*2908d778SJames Bottomley tmf_resp = sas_recover_I_T(task->dev); 448*2908d778SJames Bottomley if (tmf_resp == TMF_RESP_FUNC_COMPLETE) { 449*2908d778SJames Bottomley SAS_DPRINTK("I_T %016llx recovered\n", 450*2908d778SJames Bottomley SAS_ADDR(task->dev->sas_addr)); 451*2908d778SJames Bottomley task->task_done(task); 452*2908d778SJames Bottomley sas_scsi_clear_queue_I_T(&error_q, task->dev); 453*2908d778SJames Bottomley goto Again; 454*2908d778SJames Bottomley } 455*2908d778SJames Bottomley /* Hammer time :-) */ 456*2908d778SJames Bottomley if (i->dft->lldd_clear_nexus_port) { 457*2908d778SJames Bottomley struct asd_sas_port *port = task->dev->port; 458*2908d778SJames Bottomley SAS_DPRINTK("clearing nexus for port:%d\n", 459*2908d778SJames Bottomley port->id); 460*2908d778SJames Bottomley res = i->dft->lldd_clear_nexus_port(port); 461*2908d778SJames Bottomley if (res == TMF_RESP_FUNC_COMPLETE) { 462*2908d778SJames Bottomley SAS_DPRINTK("clear nexus port:%d " 463*2908d778SJames Bottomley "succeeded\n", port->id); 464*2908d778SJames Bottomley task->task_done(task); 465*2908d778SJames Bottomley sas_scsi_clear_queue_port(&error_q, 466*2908d778SJames Bottomley port); 467*2908d778SJames Bottomley goto Again; 468*2908d778SJames Bottomley } 469*2908d778SJames Bottomley } 470*2908d778SJames Bottomley if (i->dft->lldd_clear_nexus_ha) { 471*2908d778SJames Bottomley SAS_DPRINTK("clear nexus ha\n"); 472*2908d778SJames Bottomley res = i->dft->lldd_clear_nexus_ha(ha); 473*2908d778SJames Bottomley if (res == TMF_RESP_FUNC_COMPLETE) { 474*2908d778SJames Bottomley SAS_DPRINTK("clear nexus ha " 475*2908d778SJames Bottomley "succeeded\n"); 476*2908d778SJames Bottomley task->task_done(task); 477*2908d778SJames Bottomley goto out; 478*2908d778SJames Bottomley } 479*2908d778SJames Bottomley } 480*2908d778SJames Bottomley /* If we are here -- this means that no amount 481*2908d778SJames Bottomley * of effort could recover from errors. Quite 482*2908d778SJames Bottomley * possibly the HA just disappeared. 483*2908d778SJames Bottomley */ 484*2908d778SJames Bottomley SAS_DPRINTK("error from device %llx, LUN %x " 485*2908d778SJames Bottomley "couldn't be recovered in any way\n", 486*2908d778SJames Bottomley SAS_ADDR(task->dev->sas_addr), 487*2908d778SJames Bottomley cmd->device->lun); 488*2908d778SJames Bottomley 489*2908d778SJames Bottomley task->task_done(task); 490*2908d778SJames Bottomley goto clear_q; 491*2908d778SJames Bottomley } 492*2908d778SJames Bottomley } 493*2908d778SJames Bottomley out: 494*2908d778SJames Bottomley SAS_DPRINTK("--- Exit %s\n", __FUNCTION__); 495*2908d778SJames Bottomley return; 496*2908d778SJames Bottomley clear_q: 497*2908d778SJames Bottomley SAS_DPRINTK("--- Exit %s -- clear_q\n", __FUNCTION__); 498*2908d778SJames Bottomley list_for_each_entry_safe(cmd, n, &error_q, eh_entry) { 499*2908d778SJames Bottomley struct sas_task *task = TO_SAS_TASK(cmd); 500*2908d778SJames Bottomley list_del_init(&cmd->eh_entry); 501*2908d778SJames Bottomley task->task_done(task); 502*2908d778SJames Bottomley } 503*2908d778SJames Bottomley } 504*2908d778SJames Bottomley 505*2908d778SJames Bottomley enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) 506*2908d778SJames Bottomley { 507*2908d778SJames Bottomley struct sas_task *task = TO_SAS_TASK(cmd); 508*2908d778SJames Bottomley unsigned long flags; 509*2908d778SJames Bottomley 510*2908d778SJames Bottomley if (!task) { 511*2908d778SJames Bottomley SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n", 512*2908d778SJames Bottomley cmd, task); 513*2908d778SJames Bottomley return EH_HANDLED; 514*2908d778SJames Bottomley } 515*2908d778SJames Bottomley 516*2908d778SJames Bottomley spin_lock_irqsave(&task->task_state_lock, flags); 517*2908d778SJames Bottomley if (task->task_state_flags & SAS_TASK_STATE_DONE) { 518*2908d778SJames Bottomley spin_unlock_irqrestore(&task->task_state_lock, flags); 519*2908d778SJames Bottomley SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n", 520*2908d778SJames Bottomley cmd, task); 521*2908d778SJames Bottomley return EH_HANDLED; 522*2908d778SJames Bottomley } 523*2908d778SJames Bottomley task->task_state_flags |= SAS_TASK_STATE_ABORTED; 524*2908d778SJames Bottomley spin_unlock_irqrestore(&task->task_state_lock, flags); 525*2908d778SJames Bottomley 526*2908d778SJames Bottomley SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_NOT_HANDLED\n", 527*2908d778SJames Bottomley cmd, task); 528*2908d778SJames Bottomley 529*2908d778SJames Bottomley return EH_NOT_HANDLED; 530*2908d778SJames Bottomley } 531*2908d778SJames Bottomley 532*2908d778SJames Bottomley struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy) 533*2908d778SJames Bottomley { 534*2908d778SJames Bottomley struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent); 535*2908d778SJames Bottomley struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); 536*2908d778SJames Bottomley struct domain_device *found_dev = NULL; 537*2908d778SJames Bottomley int i; 538*2908d778SJames Bottomley 539*2908d778SJames Bottomley spin_lock(&ha->phy_port_lock); 540*2908d778SJames Bottomley for (i = 0; i < ha->num_phys; i++) { 541*2908d778SJames Bottomley struct asd_sas_port *port = ha->sas_port[i]; 542*2908d778SJames Bottomley struct domain_device *dev; 543*2908d778SJames Bottomley 544*2908d778SJames Bottomley spin_lock(&port->dev_list_lock); 545*2908d778SJames Bottomley list_for_each_entry(dev, &port->dev_list, dev_list_node) { 546*2908d778SJames Bottomley if (rphy == dev->rphy) { 547*2908d778SJames Bottomley found_dev = dev; 548*2908d778SJames Bottomley spin_unlock(&port->dev_list_lock); 549*2908d778SJames Bottomley goto found; 550*2908d778SJames Bottomley } 551*2908d778SJames Bottomley } 552*2908d778SJames Bottomley spin_unlock(&port->dev_list_lock); 553*2908d778SJames Bottomley } 554*2908d778SJames Bottomley found: 555*2908d778SJames Bottomley spin_unlock(&ha->phy_port_lock); 556*2908d778SJames Bottomley 557*2908d778SJames Bottomley return found_dev; 558*2908d778SJames Bottomley } 559*2908d778SJames Bottomley 560*2908d778SJames Bottomley static inline struct domain_device *sas_find_target(struct scsi_target *starget) 561*2908d778SJames Bottomley { 562*2908d778SJames Bottomley struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent); 563*2908d778SJames Bottomley 564*2908d778SJames Bottomley return sas_find_dev_by_rphy(rphy); 565*2908d778SJames Bottomley } 566*2908d778SJames Bottomley 567*2908d778SJames Bottomley int sas_target_alloc(struct scsi_target *starget) 568*2908d778SJames Bottomley { 569*2908d778SJames Bottomley struct domain_device *found_dev = sas_find_target(starget); 570*2908d778SJames Bottomley 571*2908d778SJames Bottomley if (!found_dev) 572*2908d778SJames Bottomley return -ENODEV; 573*2908d778SJames Bottomley 574*2908d778SJames Bottomley starget->hostdata = found_dev; 575*2908d778SJames Bottomley return 0; 576*2908d778SJames Bottomley } 577*2908d778SJames Bottomley 578*2908d778SJames Bottomley #define SAS_DEF_QD 32 579*2908d778SJames Bottomley #define SAS_MAX_QD 64 580*2908d778SJames Bottomley 581*2908d778SJames Bottomley int sas_slave_configure(struct scsi_device *scsi_dev) 582*2908d778SJames Bottomley { 583*2908d778SJames Bottomley struct domain_device *dev = sdev_to_domain_dev(scsi_dev); 584*2908d778SJames Bottomley struct sas_ha_struct *sas_ha; 585*2908d778SJames Bottomley 586*2908d778SJames Bottomley BUG_ON(dev->rphy->identify.device_type != SAS_END_DEVICE); 587*2908d778SJames Bottomley 588*2908d778SJames Bottomley sas_ha = dev->port->ha; 589*2908d778SJames Bottomley 590*2908d778SJames Bottomley sas_read_port_mode_page(scsi_dev); 591*2908d778SJames Bottomley 592*2908d778SJames Bottomley if (scsi_dev->tagged_supported) { 593*2908d778SJames Bottomley scsi_set_tag_type(scsi_dev, MSG_SIMPLE_TAG); 594*2908d778SJames Bottomley scsi_activate_tcq(scsi_dev, SAS_DEF_QD); 595*2908d778SJames Bottomley } else { 596*2908d778SJames Bottomley SAS_DPRINTK("device %llx, LUN %x doesn't support " 597*2908d778SJames Bottomley "TCQ\n", SAS_ADDR(dev->sas_addr), 598*2908d778SJames Bottomley scsi_dev->lun); 599*2908d778SJames Bottomley scsi_dev->tagged_supported = 0; 600*2908d778SJames Bottomley scsi_set_tag_type(scsi_dev, 0); 601*2908d778SJames Bottomley scsi_deactivate_tcq(scsi_dev, 1); 602*2908d778SJames Bottomley } 603*2908d778SJames Bottomley 604*2908d778SJames Bottomley return 0; 605*2908d778SJames Bottomley } 606*2908d778SJames Bottomley 607*2908d778SJames Bottomley void sas_slave_destroy(struct scsi_device *scsi_dev) 608*2908d778SJames Bottomley { 609*2908d778SJames Bottomley } 610*2908d778SJames Bottomley 611*2908d778SJames Bottomley int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth) 612*2908d778SJames Bottomley { 613*2908d778SJames Bottomley int res = min(new_depth, SAS_MAX_QD); 614*2908d778SJames Bottomley 615*2908d778SJames Bottomley if (scsi_dev->tagged_supported) 616*2908d778SJames Bottomley scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev), 617*2908d778SJames Bottomley res); 618*2908d778SJames Bottomley else { 619*2908d778SJames Bottomley struct domain_device *dev = sdev_to_domain_dev(scsi_dev); 620*2908d778SJames Bottomley sas_printk("device %llx LUN %x queue depth changed to 1\n", 621*2908d778SJames Bottomley SAS_ADDR(dev->sas_addr), 622*2908d778SJames Bottomley scsi_dev->lun); 623*2908d778SJames Bottomley scsi_adjust_queue_depth(scsi_dev, 0, 1); 624*2908d778SJames Bottomley res = 1; 625*2908d778SJames Bottomley } 626*2908d778SJames Bottomley 627*2908d778SJames Bottomley return res; 628*2908d778SJames Bottomley } 629*2908d778SJames Bottomley 630*2908d778SJames Bottomley int sas_change_queue_type(struct scsi_device *scsi_dev, int qt) 631*2908d778SJames Bottomley { 632*2908d778SJames Bottomley if (!scsi_dev->tagged_supported) 633*2908d778SJames Bottomley return 0; 634*2908d778SJames Bottomley 635*2908d778SJames Bottomley scsi_deactivate_tcq(scsi_dev, 1); 636*2908d778SJames Bottomley 637*2908d778SJames Bottomley scsi_set_tag_type(scsi_dev, qt); 638*2908d778SJames Bottomley scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth); 639*2908d778SJames Bottomley 640*2908d778SJames Bottomley return qt; 641*2908d778SJames Bottomley } 642*2908d778SJames Bottomley 643*2908d778SJames Bottomley int sas_bios_param(struct scsi_device *scsi_dev, 644*2908d778SJames Bottomley struct block_device *bdev, 645*2908d778SJames Bottomley sector_t capacity, int *hsc) 646*2908d778SJames Bottomley { 647*2908d778SJames Bottomley hsc[0] = 255; 648*2908d778SJames Bottomley hsc[1] = 63; 649*2908d778SJames Bottomley sector_div(capacity, 255*63); 650*2908d778SJames Bottomley hsc[2] = capacity; 651*2908d778SJames Bottomley 652*2908d778SJames Bottomley return 0; 653*2908d778SJames Bottomley } 654*2908d778SJames Bottomley 655*2908d778SJames Bottomley /* ---------- Task Collector Thread implementation ---------- */ 656*2908d778SJames Bottomley 657*2908d778SJames Bottomley static void sas_queue(struct sas_ha_struct *sas_ha) 658*2908d778SJames Bottomley { 659*2908d778SJames Bottomley struct scsi_core *core = &sas_ha->core; 660*2908d778SJames Bottomley unsigned long flags; 661*2908d778SJames Bottomley LIST_HEAD(q); 662*2908d778SJames Bottomley int can_queue; 663*2908d778SJames Bottomley int res; 664*2908d778SJames Bottomley struct sas_internal *i = to_sas_internal(core->shost->transportt); 665*2908d778SJames Bottomley 666*2908d778SJames Bottomley spin_lock_irqsave(&core->task_queue_lock, flags); 667*2908d778SJames Bottomley while (!core->queue_thread_kill && 668*2908d778SJames Bottomley !list_empty(&core->task_queue)) { 669*2908d778SJames Bottomley 670*2908d778SJames Bottomley can_queue = sas_ha->lldd_queue_size - core->task_queue_size; 671*2908d778SJames Bottomley if (can_queue >= 0) { 672*2908d778SJames Bottomley can_queue = core->task_queue_size; 673*2908d778SJames Bottomley list_splice_init(&core->task_queue, &q); 674*2908d778SJames Bottomley } else { 675*2908d778SJames Bottomley struct list_head *a, *n; 676*2908d778SJames Bottomley 677*2908d778SJames Bottomley can_queue = sas_ha->lldd_queue_size; 678*2908d778SJames Bottomley list_for_each_safe(a, n, &core->task_queue) { 679*2908d778SJames Bottomley list_move_tail(a, &q); 680*2908d778SJames Bottomley if (--can_queue == 0) 681*2908d778SJames Bottomley break; 682*2908d778SJames Bottomley } 683*2908d778SJames Bottomley can_queue = sas_ha->lldd_queue_size; 684*2908d778SJames Bottomley } 685*2908d778SJames Bottomley core->task_queue_size -= can_queue; 686*2908d778SJames Bottomley spin_unlock_irqrestore(&core->task_queue_lock, flags); 687*2908d778SJames Bottomley { 688*2908d778SJames Bottomley struct sas_task *task = list_entry(q.next, 689*2908d778SJames Bottomley struct sas_task, 690*2908d778SJames Bottomley list); 691*2908d778SJames Bottomley list_del_init(&q); 692*2908d778SJames Bottomley res = i->dft->lldd_execute_task(task, can_queue, 693*2908d778SJames Bottomley GFP_KERNEL); 694*2908d778SJames Bottomley if (unlikely(res)) 695*2908d778SJames Bottomley __list_add(&q, task->list.prev, &task->list); 696*2908d778SJames Bottomley } 697*2908d778SJames Bottomley spin_lock_irqsave(&core->task_queue_lock, flags); 698*2908d778SJames Bottomley if (res) { 699*2908d778SJames Bottomley list_splice_init(&q, &core->task_queue); /*at head*/ 700*2908d778SJames Bottomley core->task_queue_size += can_queue; 701*2908d778SJames Bottomley } 702*2908d778SJames Bottomley } 703*2908d778SJames Bottomley spin_unlock_irqrestore(&core->task_queue_lock, flags); 704*2908d778SJames Bottomley } 705*2908d778SJames Bottomley 706*2908d778SJames Bottomley static DECLARE_COMPLETION(queue_th_comp); 707*2908d778SJames Bottomley 708*2908d778SJames Bottomley /** 709*2908d778SJames Bottomley * sas_queue_thread -- The Task Collector thread 710*2908d778SJames Bottomley * @_sas_ha: pointer to struct sas_ha 711*2908d778SJames Bottomley */ 712*2908d778SJames Bottomley static int sas_queue_thread(void *_sas_ha) 713*2908d778SJames Bottomley { 714*2908d778SJames Bottomley struct sas_ha_struct *sas_ha = _sas_ha; 715*2908d778SJames Bottomley struct scsi_core *core = &sas_ha->core; 716*2908d778SJames Bottomley 717*2908d778SJames Bottomley daemonize("sas_queue_%d", core->shost->host_no); 718*2908d778SJames Bottomley current->flags |= PF_NOFREEZE; 719*2908d778SJames Bottomley 720*2908d778SJames Bottomley complete(&queue_th_comp); 721*2908d778SJames Bottomley 722*2908d778SJames Bottomley while (1) { 723*2908d778SJames Bottomley down_interruptible(&core->queue_thread_sema); 724*2908d778SJames Bottomley sas_queue(sas_ha); 725*2908d778SJames Bottomley if (core->queue_thread_kill) 726*2908d778SJames Bottomley break; 727*2908d778SJames Bottomley } 728*2908d778SJames Bottomley 729*2908d778SJames Bottomley complete(&queue_th_comp); 730*2908d778SJames Bottomley 731*2908d778SJames Bottomley return 0; 732*2908d778SJames Bottomley } 733*2908d778SJames Bottomley 734*2908d778SJames Bottomley int sas_init_queue(struct sas_ha_struct *sas_ha) 735*2908d778SJames Bottomley { 736*2908d778SJames Bottomley int res; 737*2908d778SJames Bottomley struct scsi_core *core = &sas_ha->core; 738*2908d778SJames Bottomley 739*2908d778SJames Bottomley spin_lock_init(&core->task_queue_lock); 740*2908d778SJames Bottomley core->task_queue_size = 0; 741*2908d778SJames Bottomley INIT_LIST_HEAD(&core->task_queue); 742*2908d778SJames Bottomley init_MUTEX_LOCKED(&core->queue_thread_sema); 743*2908d778SJames Bottomley 744*2908d778SJames Bottomley res = kernel_thread(sas_queue_thread, sas_ha, 0); 745*2908d778SJames Bottomley if (res >= 0) 746*2908d778SJames Bottomley wait_for_completion(&queue_th_comp); 747*2908d778SJames Bottomley 748*2908d778SJames Bottomley return res < 0 ? res : 0; 749*2908d778SJames Bottomley } 750*2908d778SJames Bottomley 751*2908d778SJames Bottomley void sas_shutdown_queue(struct sas_ha_struct *sas_ha) 752*2908d778SJames Bottomley { 753*2908d778SJames Bottomley unsigned long flags; 754*2908d778SJames Bottomley struct scsi_core *core = &sas_ha->core; 755*2908d778SJames Bottomley struct sas_task *task, *n; 756*2908d778SJames Bottomley 757*2908d778SJames Bottomley init_completion(&queue_th_comp); 758*2908d778SJames Bottomley core->queue_thread_kill = 1; 759*2908d778SJames Bottomley up(&core->queue_thread_sema); 760*2908d778SJames Bottomley wait_for_completion(&queue_th_comp); 761*2908d778SJames Bottomley 762*2908d778SJames Bottomley if (!list_empty(&core->task_queue)) 763*2908d778SJames Bottomley SAS_DPRINTK("HA: %llx: scsi core task queue is NOT empty!?\n", 764*2908d778SJames Bottomley SAS_ADDR(sas_ha->sas_addr)); 765*2908d778SJames Bottomley 766*2908d778SJames Bottomley spin_lock_irqsave(&core->task_queue_lock, flags); 767*2908d778SJames Bottomley list_for_each_entry_safe(task, n, &core->task_queue, list) { 768*2908d778SJames Bottomley struct scsi_cmnd *cmd = task->uldd_task; 769*2908d778SJames Bottomley 770*2908d778SJames Bottomley list_del_init(&task->list); 771*2908d778SJames Bottomley 772*2908d778SJames Bottomley ASSIGN_SAS_TASK(cmd, NULL); 773*2908d778SJames Bottomley sas_free_task(task); 774*2908d778SJames Bottomley cmd->result = DID_ABORT << 16; 775*2908d778SJames Bottomley cmd->scsi_done(cmd); 776*2908d778SJames Bottomley } 777*2908d778SJames Bottomley spin_unlock_irqrestore(&core->task_queue_lock, flags); 778*2908d778SJames Bottomley } 779*2908d778SJames Bottomley 780*2908d778SJames Bottomley EXPORT_SYMBOL_GPL(sas_queuecommand); 781*2908d778SJames Bottomley EXPORT_SYMBOL_GPL(sas_target_alloc); 782*2908d778SJames Bottomley EXPORT_SYMBOL_GPL(sas_slave_configure); 783*2908d778SJames Bottomley EXPORT_SYMBOL_GPL(sas_slave_destroy); 784*2908d778SJames Bottomley EXPORT_SYMBOL_GPL(sas_change_queue_depth); 785*2908d778SJames Bottomley EXPORT_SYMBOL_GPL(sas_change_queue_type); 786*2908d778SJames Bottomley EXPORT_SYMBOL_GPL(sas_bios_param); 787