xref: /openbmc/linux/drivers/scsi/libsas/sas_scsi_host.c (revision 2908d778ab3e244900c310974e1fc1c69066e450)
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