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