Commit | Line | Data |
---|---|---|
6f231dda DW |
1 | /* |
2 | * This file is provided under a dual BSD/GPLv2 license. When using or | |
3 | * redistributing this file, you may do so under either license. | |
4 | * | |
5 | * GPL LICENSE SUMMARY | |
6 | * | |
7 | * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of version 2 of the GNU General Public License as | |
11 | * published by the Free Software Foundation. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | * The full GNU General Public License is included in this distribution | |
22 | * in the file called LICENSE.GPL. | |
23 | * | |
24 | * BSD LICENSE | |
25 | * | |
26 | * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. | |
27 | * All rights reserved. | |
28 | * | |
29 | * Redistribution and use in source and binary forms, with or without | |
30 | * modification, are permitted provided that the following conditions | |
31 | * are met: | |
32 | * | |
33 | * * Redistributions of source code must retain the above copyright | |
34 | * notice, this list of conditions and the following disclaimer. | |
35 | * * Redistributions in binary form must reproduce the above copyright | |
36 | * notice, this list of conditions and the following disclaimer in | |
37 | * the documentation and/or other materials provided with the | |
38 | * distribution. | |
39 | * * Neither the name of Intel Corporation nor the names of its | |
40 | * contributors may be used to endorse or promote products derived | |
41 | * from this software without specific prior written permission. | |
42 | * | |
43 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
44 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
45 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
46 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
47 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
48 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
49 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
50 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
51 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
52 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
53 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
54 | */ | |
55 | ||
56 | #include <linux/completion.h> | |
50e7f9b5 | 57 | #include <linux/irqflags.h> |
af5ae893 | 58 | #include "sas.h" |
61aaff49 | 59 | #include <scsi/libsas.h> |
88f3b62a DW |
60 | #include "remote_device.h" |
61 | #include "remote_node_context.h" | |
6f231dda DW |
62 | #include "isci.h" |
63 | #include "request.h" | |
6f231dda | 64 | #include "task.h" |
312e0c24 | 65 | #include "host.h" |
af5ae893 | 66 | |
50e7f9b5 | 67 | /** |
1077a574 DW |
68 | * isci_task_refuse() - complete the request to the upper layer driver in |
69 | * the case where an I/O needs to be completed back in the submit path. | |
70 | * @ihost: host on which the the request was queued | |
71 | * @task: request to complete | |
72 | * @response: response code for the completed task. | |
73 | * @status: status code for the completed task. | |
50e7f9b5 | 74 | * |
50e7f9b5 | 75 | */ |
1077a574 DW |
76 | static void isci_task_refuse(struct isci_host *ihost, struct sas_task *task, |
77 | enum service_response response, | |
78 | enum exec_status status) | |
79 | ||
50e7f9b5 | 80 | { |
14aaa9f0 | 81 | unsigned long flags; |
50e7f9b5 | 82 | |
14aaa9f0 JS |
83 | /* Normal notification (task_done) */ |
84 | dev_dbg(&ihost->pdev->dev, "%s: task = %p, response=%d, status=%d\n", | |
85 | __func__, task, response, status); | |
50e7f9b5 | 86 | |
14aaa9f0 | 87 | spin_lock_irqsave(&task->task_state_lock, flags); |
50e7f9b5 | 88 | |
14aaa9f0 JS |
89 | task->task_status.resp = response; |
90 | task->task_status.stat = status; | |
50e7f9b5 | 91 | |
14aaa9f0 JS |
92 | /* Normal notification (task_done) */ |
93 | task->task_state_flags |= SAS_TASK_STATE_DONE; | |
94 | task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | | |
95 | SAS_TASK_STATE_PENDING); | |
96 | task->lldd_task = NULL; | |
97 | spin_unlock_irqrestore(&task->task_state_lock, flags); | |
98 | ||
99 | task->task_done(task); | |
50e7f9b5 | 100 | } |
6f231dda | 101 | |
1077a574 DW |
102 | #define for_each_sas_task(num, task) \ |
103 | for (; num > 0; num--,\ | |
104 | task = list_entry(task->list.next, struct sas_task, list)) | |
105 | ||
9274f45e JS |
106 | |
107 | static inline int isci_device_io_ready(struct isci_remote_device *idev, | |
108 | struct sas_task *task) | |
109 | { | |
110 | return idev ? test_bit(IDEV_IO_READY, &idev->flags) || | |
111 | (test_bit(IDEV_IO_NCQERROR, &idev->flags) && | |
112 | isci_task_is_ncq_recovery(task)) | |
113 | : 0; | |
114 | } | |
6f231dda DW |
115 | /** |
116 | * isci_task_execute_task() - This function is one of the SAS Domain Template | |
117 | * functions. This function is called by libsas to send a task down to | |
118 | * hardware. | |
119 | * @task: This parameter specifies the SAS task to send. | |
120 | * @num: This parameter specifies the number of tasks to queue. | |
121 | * @gfp_flags: This parameter specifies the context of this call. | |
122 | * | |
123 | * status, zero indicates success. | |
124 | */ | |
125 | int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags) | |
126 | { | |
4393aa4e | 127 | struct isci_host *ihost = dev_to_ihost(task->dev); |
209fae14 | 128 | struct isci_remote_device *idev; |
6f231dda | 129 | unsigned long flags; |
f2088267 | 130 | bool io_ready; |
312e0c24 | 131 | u16 tag; |
6f231dda | 132 | |
1077a574 | 133 | dev_dbg(&ihost->pdev->dev, "%s: num=%d\n", __func__, num); |
6f231dda | 134 | |
1077a574 | 135 | for_each_sas_task(num, task) { |
312e0c24 DW |
136 | enum sci_status status = SCI_FAILURE; |
137 | ||
209fae14 DW |
138 | spin_lock_irqsave(&ihost->scic_lock, flags); |
139 | idev = isci_lookup_device(task->dev); | |
9274f45e | 140 | io_ready = isci_device_io_ready(idev, task); |
312e0c24 | 141 | tag = isci_alloc_tag(ihost); |
209fae14 | 142 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
6f231dda | 143 | |
f2088267 DW |
144 | dev_dbg(&ihost->pdev->dev, |
145 | "task: %p, num: %d dev: %p idev: %p:%#lx cmd = %p\n", | |
146 | task, num, task->dev, idev, idev ? idev->flags : 0, | |
147 | task->uldd_task); | |
6f231dda | 148 | |
f2088267 DW |
149 | if (!idev) { |
150 | isci_task_refuse(ihost, task, SAS_TASK_UNDELIVERED, | |
151 | SAS_DEVICE_UNKNOWN); | |
312e0c24 | 152 | } else if (!io_ready || tag == SCI_CONTROLLER_INVALID_IO_TAG) { |
f2088267 DW |
153 | /* Indicate QUEUE_FULL so that the scsi midlayer |
154 | * retries. | |
155 | */ | |
156 | isci_task_refuse(ihost, task, SAS_TASK_COMPLETE, | |
157 | SAS_QUEUE_FULL); | |
f0846c68 JS |
158 | } else { |
159 | /* There is a device and it's ready for I/O. */ | |
160 | spin_lock_irqsave(&task->task_state_lock, flags); | |
6f231dda | 161 | |
f0846c68 | 162 | if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { |
f2088267 | 163 | /* The I/O was aborted. */ |
f0846c68 JS |
164 | spin_unlock_irqrestore(&task->task_state_lock, |
165 | flags); | |
6f231dda | 166 | |
1077a574 DW |
167 | isci_task_refuse(ihost, task, |
168 | SAS_TASK_UNDELIVERED, | |
169 | SAM_STAT_TASK_ABORTED); | |
f0846c68 | 170 | } else { |
6f231dda DW |
171 | task->task_state_flags |= SAS_TASK_AT_INITIATOR; |
172 | spin_unlock_irqrestore(&task->task_state_lock, flags); | |
f0846c68 JS |
173 | |
174 | /* build and send the request. */ | |
db056250 | 175 | status = isci_request_execute(ihost, idev, task, tag); |
f0846c68 JS |
176 | |
177 | if (status != SCI_SUCCESS) { | |
178 | ||
179 | spin_lock_irqsave(&task->task_state_lock, flags); | |
180 | /* Did not really start this command. */ | |
181 | task->task_state_flags &= ~SAS_TASK_AT_INITIATOR; | |
182 | spin_unlock_irqrestore(&task->task_state_lock, flags); | |
183 | ||
c2cb8a5f JS |
184 | if (test_bit(IDEV_GONE, &idev->flags)) { |
185 | ||
186 | /* Indicate that the device | |
187 | * is gone. | |
188 | */ | |
189 | isci_task_refuse(ihost, task, | |
190 | SAS_TASK_UNDELIVERED, | |
191 | SAS_DEVICE_UNKNOWN); | |
192 | } else { | |
193 | /* Indicate QUEUE_FULL so that | |
194 | * the scsi midlayer retries. | |
195 | * If the request failed for | |
196 | * remote device reasons, it | |
197 | * gets returned as | |
198 | * SAS_TASK_UNDELIVERED next | |
199 | * time through. | |
200 | */ | |
201 | isci_task_refuse(ihost, task, | |
202 | SAS_TASK_COMPLETE, | |
203 | SAS_QUEUE_FULL); | |
204 | } | |
f0846c68 | 205 | } |
6f231dda DW |
206 | } |
207 | } | |
312e0c24 DW |
208 | if (status != SCI_SUCCESS && tag != SCI_CONTROLLER_INVALID_IO_TAG) { |
209 | spin_lock_irqsave(&ihost->scic_lock, flags); | |
210 | /* command never hit the device, so just free | |
211 | * the tci and skip the sequence increment | |
212 | */ | |
213 | isci_tci_free(ihost, ISCI_TAG_TCI(tag)); | |
214 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | |
215 | } | |
209fae14 | 216 | isci_put_device(idev); |
1077a574 | 217 | } |
6f231dda DW |
218 | return 0; |
219 | } | |
220 | ||
0d0cf14c | 221 | static struct isci_request *isci_task_request_build(struct isci_host *ihost, |
209fae14 | 222 | struct isci_remote_device *idev, |
312e0c24 | 223 | u16 tag, struct isci_tmf *isci_tmf) |
6f231dda | 224 | { |
6f231dda | 225 | enum sci_status status = SCI_FAILURE; |
0d0cf14c | 226 | struct isci_request *ireq = NULL; |
a1a113b0 | 227 | struct domain_device *dev; |
6f231dda | 228 | |
0d0cf14c | 229 | dev_dbg(&ihost->pdev->dev, |
6f231dda DW |
230 | "%s: isci_tmf = %p\n", __func__, isci_tmf); |
231 | ||
0d0cf14c | 232 | dev = idev->domain_dev; |
6f231dda DW |
233 | |
234 | /* do common allocation and init of request object. */ | |
db056250 | 235 | ireq = isci_tmf_request_from_tag(ihost, isci_tmf, tag); |
0d0cf14c DW |
236 | if (!ireq) |
237 | return NULL; | |
6f231dda DW |
238 | |
239 | /* let the core do it's construct. */ | |
89a7301f | 240 | status = sci_task_request_construct(ihost, idev, tag, |
5076a1a9 | 241 | ireq); |
6f231dda DW |
242 | |
243 | if (status != SCI_SUCCESS) { | |
0d0cf14c | 244 | dev_warn(&ihost->pdev->dev, |
89a7301f | 245 | "%s: sci_task_request_construct failed - " |
6f231dda DW |
246 | "status = 0x%x\n", |
247 | __func__, | |
248 | status); | |
db056250 | 249 | return NULL; |
6f231dda DW |
250 | } |
251 | ||
a1a113b0 DW |
252 | /* XXX convert to get this from task->tproto like other drivers */ |
253 | if (dev->dev_type == SAS_END_DEV) { | |
6f231dda | 254 | isci_tmf->proto = SAS_PROTOCOL_SSP; |
89a7301f | 255 | status = sci_task_request_construct_ssp(ireq); |
6f231dda | 256 | if (status != SCI_SUCCESS) |
db056250 | 257 | return NULL; |
6f231dda DW |
258 | } |
259 | ||
0d0cf14c | 260 | return ireq; |
6f231dda DW |
261 | } |
262 | ||
16ba7709 DW |
263 | static int isci_task_execute_tmf(struct isci_host *ihost, |
264 | struct isci_remote_device *idev, | |
265 | struct isci_tmf *tmf, unsigned long timeout_ms) | |
6f231dda DW |
266 | { |
267 | DECLARE_COMPLETION_ONSTACK(completion); | |
467e855a | 268 | enum sci_task_status status = SCI_TASK_FAILURE; |
0d0cf14c | 269 | struct isci_request *ireq; |
6f231dda DW |
270 | int ret = TMF_RESP_FUNC_FAILED; |
271 | unsigned long flags; | |
fd18388b | 272 | unsigned long timeleft; |
312e0c24 DW |
273 | u16 tag; |
274 | ||
275 | spin_lock_irqsave(&ihost->scic_lock, flags); | |
276 | tag = isci_alloc_tag(ihost); | |
277 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | |
278 | ||
279 | if (tag == SCI_CONTROLLER_INVALID_IO_TAG) | |
280 | return ret; | |
6f231dda DW |
281 | |
282 | /* sanity check, return TMF_RESP_FUNC_FAILED | |
283 | * if the device is not there and ready. | |
284 | */ | |
78a6f06e DW |
285 | if (!idev || |
286 | (!test_bit(IDEV_IO_READY, &idev->flags) && | |
287 | !test_bit(IDEV_IO_NCQERROR, &idev->flags))) { | |
0d0cf14c | 288 | dev_dbg(&ihost->pdev->dev, |
78a6f06e | 289 | "%s: idev = %p not ready (%#lx)\n", |
6f231dda | 290 | __func__, |
78a6f06e | 291 | idev, idev ? idev->flags : 0); |
312e0c24 | 292 | goto err_tci; |
6f231dda | 293 | } else |
0d0cf14c | 294 | dev_dbg(&ihost->pdev->dev, |
78a6f06e DW |
295 | "%s: idev = %p\n", |
296 | __func__, idev); | |
6f231dda DW |
297 | |
298 | /* Assign the pointer to the TMF's completion kernel wait structure. */ | |
299 | tmf->complete = &completion; | |
b343dff1 | 300 | tmf->status = SCI_FAILURE_TIMEOUT; |
6f231dda | 301 | |
78a6f06e | 302 | ireq = isci_task_request_build(ihost, idev, tag, tmf); |
312e0c24 DW |
303 | if (!ireq) |
304 | goto err_tci; | |
6f231dda | 305 | |
0d0cf14c | 306 | spin_lock_irqsave(&ihost->scic_lock, flags); |
6f231dda DW |
307 | |
308 | /* start the TMF io. */ | |
89a7301f | 309 | status = sci_controller_start_task(ihost, idev, ireq); |
6f231dda | 310 | |
467e855a | 311 | if (status != SCI_TASK_SUCCESS) { |
a8a0a133 | 312 | dev_dbg(&ihost->pdev->dev, |
6f231dda DW |
313 | "%s: start_io failed - status = 0x%x, request = %p\n", |
314 | __func__, | |
315 | status, | |
0d0cf14c DW |
316 | ireq); |
317 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | |
db056250 | 318 | goto err_tci; |
6f231dda | 319 | } |
0d0cf14c | 320 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
6f231dda | 321 | |
31a38ef0 JS |
322 | /* The RNC must be unsuspended before the TMF can get a response. */ |
323 | isci_remote_device_resume_from_abort(ihost, idev); | |
324 | ||
6f231dda | 325 | /* Wait for the TMF to complete, or a timeout. */ |
fd18388b | 326 | timeleft = wait_for_completion_timeout(&completion, |
086a0dab | 327 | msecs_to_jiffies(timeout_ms)); |
fd18388b EN |
328 | |
329 | if (timeleft == 0) { | |
b343dff1 JS |
330 | /* The TMF did not complete - this could be because |
331 | * of an unplug. Terminate the TMF request now. | |
332 | */ | |
14aaa9f0 | 333 | isci_remote_device_suspend_terminate(ihost, idev, ireq); |
fd18388b | 334 | } |
6f231dda | 335 | |
27234ab4 | 336 | isci_print_tmf(ihost, tmf); |
6f231dda DW |
337 | |
338 | if (tmf->status == SCI_SUCCESS) | |
339 | ret = TMF_RESP_FUNC_COMPLETE; | |
340 | else if (tmf->status == SCI_FAILURE_IO_RESPONSE_VALID) { | |
0d0cf14c | 341 | dev_dbg(&ihost->pdev->dev, |
6f231dda DW |
342 | "%s: tmf.status == " |
343 | "SCI_FAILURE_IO_RESPONSE_VALID\n", | |
344 | __func__); | |
345 | ret = TMF_RESP_FUNC_COMPLETE; | |
346 | } | |
347 | /* Else - leave the default "failed" status alone. */ | |
348 | ||
0d0cf14c | 349 | dev_dbg(&ihost->pdev->dev, |
6f231dda DW |
350 | "%s: completed request = %p\n", |
351 | __func__, | |
0d0cf14c | 352 | ireq); |
6f231dda | 353 | |
6f231dda | 354 | return ret; |
312e0c24 | 355 | |
312e0c24 DW |
356 | err_tci: |
357 | spin_lock_irqsave(&ihost->scic_lock, flags); | |
358 | isci_tci_free(ihost, ISCI_TAG_TCI(tag)); | |
359 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | |
360 | ||
361 | return ret; | |
6f231dda DW |
362 | } |
363 | ||
16ba7709 | 364 | static void isci_task_build_tmf(struct isci_tmf *tmf, |
14aaa9f0 | 365 | enum isci_tmf_function_codes code) |
6f231dda | 366 | { |
6f231dda | 367 | memset(tmf, 0, sizeof(*tmf)); |
14aaa9f0 | 368 | tmf->tmf_code = code; |
c3f42feb | 369 | } |
1fad9e93 | 370 | |
16ba7709 DW |
371 | static void isci_task_build_abort_task_tmf(struct isci_tmf *tmf, |
372 | enum isci_tmf_function_codes code, | |
16ba7709 | 373 | struct isci_request *old_request) |
c3f42feb | 374 | { |
14aaa9f0 | 375 | isci_task_build_tmf(tmf, code); |
c3f42feb | 376 | tmf->io_tag = old_request->io_tag; |
6f231dda DW |
377 | } |
378 | ||
6f231dda DW |
379 | /** |
380 | * isci_task_send_lu_reset_sas() - This function is called by of the SAS Domain | |
381 | * Template functions. | |
382 | * @lun: This parameter specifies the lun to be reset. | |
383 | * | |
384 | * status, zero indicates success. | |
385 | */ | |
386 | static int isci_task_send_lu_reset_sas( | |
387 | struct isci_host *isci_host, | |
388 | struct isci_remote_device *isci_device, | |
389 | u8 *lun) | |
390 | { | |
391 | struct isci_tmf tmf; | |
392 | int ret = TMF_RESP_FUNC_FAILED; | |
393 | ||
394 | dev_dbg(&isci_host->pdev->dev, | |
395 | "%s: isci_host = %p, isci_device = %p\n", | |
396 | __func__, isci_host, isci_device); | |
397 | /* Send the LUN reset to the target. By the time the call returns, | |
398 | * the TMF has fully exected in the target (in which case the return | |
399 | * value is "TMF_RESP_FUNC_COMPLETE", or the request timed-out (or | |
400 | * was otherwise unable to be executed ("TMF_RESP_FUNC_FAILED"). | |
401 | */ | |
14aaa9f0 | 402 | isci_task_build_tmf(&tmf, isci_tmf_ssp_lun_reset); |
6f231dda DW |
403 | |
404 | #define ISCI_LU_RESET_TIMEOUT_MS 2000 /* 2 second timeout. */ | |
209fae14 | 405 | ret = isci_task_execute_tmf(isci_host, isci_device, &tmf, ISCI_LU_RESET_TIMEOUT_MS); |
6f231dda DW |
406 | |
407 | if (ret == TMF_RESP_FUNC_COMPLETE) | |
408 | dev_dbg(&isci_host->pdev->dev, | |
409 | "%s: %p: TMF_LU_RESET passed\n", | |
410 | __func__, isci_device); | |
411 | else | |
412 | dev_dbg(&isci_host->pdev->dev, | |
413 | "%s: %p: TMF_LU_RESET failed (%x)\n", | |
414 | __func__, isci_device, ret); | |
415 | ||
416 | return ret; | |
417 | } | |
418 | ||
43a5ab15 | 419 | int isci_task_lu_reset(struct domain_device *dev, u8 *lun) |
16ba7709 | 420 | { |
5b6bf225 | 421 | struct isci_host *ihost = dev_to_ihost(dev); |
14aaa9f0 | 422 | struct isci_remote_device *idev; |
209fae14 | 423 | unsigned long flags; |
397497dd | 424 | int ret = TMF_RESP_FUNC_COMPLETE; |
6f231dda | 425 | |
5b6bf225 | 426 | spin_lock_irqsave(&ihost->scic_lock, flags); |
63732502 | 427 | idev = isci_get_device(dev->lldd_dev); |
5b6bf225 | 428 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
6f231dda | 429 | |
5b6bf225 | 430 | dev_dbg(&ihost->pdev->dev, |
4393aa4e | 431 | "%s: domain_device=%p, isci_host=%p; isci_device=%p\n", |
14aaa9f0 | 432 | __func__, dev, ihost, idev); |
6f231dda | 433 | |
14aaa9f0 | 434 | if (!idev) { |
d80ecd57 | 435 | /* If the device is gone, escalate to I_T_Nexus_Reset. */ |
5b6bf225 | 436 | dev_dbg(&ihost->pdev->dev, "%s: No dev\n", __func__); |
6f231dda | 437 | |
d80ecd57 | 438 | ret = TMF_RESP_FUNC_FAILED; |
209fae14 | 439 | goto out; |
6f231dda DW |
440 | } |
441 | ||
08c031e4 JS |
442 | /* Suspend the RNC, kill all TCs */ |
443 | if (isci_remote_device_suspend_terminate(ihost, idev, NULL) | |
444 | != SCI_SUCCESS) { | |
445 | /* The suspend/terminate only fails if isci_get_device fails */ | |
446 | ret = TMF_RESP_FUNC_FAILED; | |
447 | goto out; | |
448 | } | |
449 | /* All pending I/Os have been terminated and cleaned up. */ | |
397497dd JS |
450 | if (!test_bit(IDEV_GONE, &idev->flags)) { |
451 | if (dev_is_sata(dev)) | |
452 | sas_ata_schedule_reset(dev); | |
453 | else | |
454 | /* Send the task management part of the reset. */ | |
455 | ret = isci_task_send_lu_reset_sas(ihost, idev, lun); | |
5b6bf225 | 456 | } |
209fae14 | 457 | out: |
14aaa9f0 | 458 | isci_put_device(idev); |
6f231dda DW |
459 | return ret; |
460 | } | |
461 | ||
462 | ||
463 | /* int (*lldd_clear_nexus_port)(struct asd_sas_port *); */ | |
464 | int isci_task_clear_nexus_port(struct asd_sas_port *port) | |
465 | { | |
466 | return TMF_RESP_FUNC_FAILED; | |
467 | } | |
468 | ||
469 | ||
470 | ||
471 | int isci_task_clear_nexus_ha(struct sas_ha_struct *ha) | |
472 | { | |
473 | return TMF_RESP_FUNC_FAILED; | |
474 | } | |
475 | ||
6f231dda DW |
476 | /* Task Management Functions. Must be called from process context. */ |
477 | ||
6f231dda DW |
478 | /** |
479 | * isci_task_abort_task() - This function is one of the SAS Domain Template | |
480 | * functions. This function is called by libsas to abort a specified task. | |
481 | * @task: This parameter specifies the SAS task to abort. | |
482 | * | |
483 | * status, zero indicates success. | |
484 | */ | |
485 | int isci_task_abort_task(struct sas_task *task) | |
486 | { | |
14aaa9f0 | 487 | struct isci_host *ihost = dev_to_ihost(task->dev); |
6f231dda | 488 | DECLARE_COMPLETION_ONSTACK(aborted_io_completion); |
a5fde225 | 489 | struct isci_request *old_request = NULL; |
14aaa9f0 | 490 | struct isci_remote_device *idev = NULL; |
a5fde225 JS |
491 | struct isci_tmf tmf; |
492 | int ret = TMF_RESP_FUNC_FAILED; | |
493 | unsigned long flags; | |
6f231dda DW |
494 | |
495 | /* Get the isci_request reference from the task. Note that | |
496 | * this check does not depend on the pending request list | |
497 | * in the device, because tasks driving resets may land here | |
498 | * after completion in the core. | |
499 | */ | |
14aaa9f0 | 500 | spin_lock_irqsave(&ihost->scic_lock, flags); |
209fae14 DW |
501 | spin_lock(&task->task_state_lock); |
502 | ||
503 | old_request = task->lldd_task; | |
504 | ||
505 | /* If task is already done, the request isn't valid */ | |
506 | if (!(task->task_state_flags & SAS_TASK_STATE_DONE) && | |
507 | (task->task_state_flags & SAS_TASK_AT_INITIATOR) && | |
508 | old_request) | |
63732502 | 509 | idev = isci_get_device(task->dev->lldd_dev); |
209fae14 DW |
510 | |
511 | spin_unlock(&task->task_state_lock); | |
14aaa9f0 | 512 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
6f231dda | 513 | |
14aaa9f0 | 514 | dev_warn(&ihost->pdev->dev, |
397497dd JS |
515 | "%s: dev = %p (%s%s), task = %p, old_request == %p\n", |
516 | __func__, idev, | |
517 | (dev_is_sata(task->dev) ? "STP/SATA" | |
518 | : ((dev_is_expander(task->dev)) | |
519 | ? "SMP" | |
520 | : "SSP")), | |
521 | ((idev) ? ((test_bit(IDEV_GONE, &idev->flags)) | |
522 | ? " IDEV_GONE" | |
523 | : "") | |
524 | : " <NULL>"), | |
525 | task, old_request); | |
6f231dda | 526 | |
98145cb7 JS |
527 | /* Device reset conditions signalled in task_state_flags are the |
528 | * responsbility of libsas to observe at the start of the error | |
529 | * handler thread. | |
6f231dda | 530 | */ |
14aaa9f0 | 531 | if (!idev || !old_request) { |
98145cb7 JS |
532 | /* The request has already completed and there |
533 | * is nothing to do here other than to set the task | |
534 | * done bit, and indicate that the task abort function | |
4907cb7b | 535 | * was successful. |
98145cb7 JS |
536 | */ |
537 | spin_lock_irqsave(&task->task_state_lock, flags); | |
538 | task->task_state_flags |= SAS_TASK_STATE_DONE; | |
539 | task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | | |
540 | SAS_TASK_STATE_PENDING); | |
541 | spin_unlock_irqrestore(&task->task_state_lock, flags); | |
6f231dda | 542 | |
98145cb7 | 543 | ret = TMF_RESP_FUNC_COMPLETE; |
6f231dda | 544 | |
14aaa9f0 JS |
545 | dev_warn(&ihost->pdev->dev, |
546 | "%s: abort task not needed for %p\n", | |
547 | __func__, task); | |
209fae14 | 548 | goto out; |
a5ec7f86 | 549 | } |
14aaa9f0 JS |
550 | /* Suspend the RNC, kill the TC */ |
551 | if (isci_remote_device_suspend_terminate(ihost, idev, old_request) | |
552 | != SCI_SUCCESS) { | |
553 | dev_warn(&ihost->pdev->dev, | |
554 | "%s: isci_remote_device_reset_terminate(dev=%p, " | |
555 | "req=%p, task=%p) failed\n", | |
556 | __func__, idev, old_request, task); | |
557 | ret = TMF_RESP_FUNC_FAILED; | |
209fae14 | 558 | goto out; |
6f231dda | 559 | } |
14aaa9f0 JS |
560 | spin_lock_irqsave(&ihost->scic_lock, flags); |
561 | ||
38d8879b | 562 | if (task->task_proto == SAS_PROTOCOL_SMP || |
98145cb7 | 563 | sas_protocol_ata(task->task_proto) || |
397497dd JS |
564 | test_bit(IREQ_COMPLETE_IN_TARGET, &old_request->flags) || |
565 | test_bit(IDEV_GONE, &idev->flags)) { | |
6f231dda | 566 | |
14aaa9f0 | 567 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
6f231dda | 568 | |
31a38ef0 JS |
569 | /* No task to send, so explicitly resume the device here */ |
570 | isci_remote_device_resume_from_abort(ihost, idev); | |
571 | ||
14aaa9f0 JS |
572 | dev_warn(&ihost->pdev->dev, |
573 | "%s: %s request" | |
397497dd JS |
574 | " or complete_in_target (%d), " |
575 | "or IDEV_GONE (%d), thus no TMF\n", | |
14aaa9f0 JS |
576 | __func__, |
577 | ((task->task_proto == SAS_PROTOCOL_SMP) | |
578 | ? "SMP" | |
579 | : (sas_protocol_ata(task->task_proto) | |
580 | ? "SATA/STP" | |
581 | : "<other>") | |
582 | ), | |
583 | test_bit(IREQ_COMPLETE_IN_TARGET, | |
397497dd JS |
584 | &old_request->flags), |
585 | test_bit(IDEV_GONE, &idev->flags)); | |
5b6bf225 | 586 | |
14aaa9f0 JS |
587 | spin_lock_irqsave(&task->task_state_lock, flags); |
588 | task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | | |
589 | SAS_TASK_STATE_PENDING); | |
590 | task->task_state_flags |= SAS_TASK_STATE_DONE; | |
591 | spin_unlock_irqrestore(&task->task_state_lock, flags); | |
592 | ||
593 | ret = TMF_RESP_FUNC_COMPLETE; | |
6f231dda DW |
594 | } else { |
595 | /* Fill in the tmf stucture */ | |
209fae14 | 596 | isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort, |
c3f42feb | 597 | old_request); |
6f231dda | 598 | |
14aaa9f0 | 599 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
5b6bf225 | 600 | |
14aaa9f0 | 601 | /* Send the task management request. */ |
98145cb7 | 602 | #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */ |
14aaa9f0 | 603 | ret = isci_task_execute_tmf(ihost, idev, &tmf, |
6f231dda | 604 | ISCI_ABORT_TASK_TIMEOUT_MS); |
a5fde225 | 605 | } |
14aaa9f0 | 606 | out: |
63732502 JS |
607 | dev_warn(&ihost->pdev->dev, |
608 | "%s: Done; dev = %p, task = %p , old_request == %p\n", | |
609 | __func__, idev, task, old_request); | |
14aaa9f0 | 610 | isci_put_device(idev); |
6f231dda DW |
611 | return ret; |
612 | } | |
613 | ||
614 | /** | |
615 | * isci_task_abort_task_set() - This function is one of the SAS Domain Template | |
616 | * functions. This is one of the Task Management functoins called by libsas, | |
617 | * to abort all task for the given lun. | |
618 | * @d_device: This parameter specifies the domain device associated with this | |
619 | * request. | |
620 | * @lun: This parameter specifies the lun associated with this request. | |
621 | * | |
622 | * status, zero indicates success. | |
623 | */ | |
624 | int isci_task_abort_task_set( | |
625 | struct domain_device *d_device, | |
626 | u8 *lun) | |
627 | { | |
628 | return TMF_RESP_FUNC_FAILED; | |
629 | } | |
630 | ||
631 | ||
632 | /** | |
633 | * isci_task_clear_aca() - This function is one of the SAS Domain Template | |
634 | * functions. This is one of the Task Management functoins called by libsas. | |
635 | * @d_device: This parameter specifies the domain device associated with this | |
636 | * request. | |
637 | * @lun: This parameter specifies the lun associated with this request. | |
638 | * | |
639 | * status, zero indicates success. | |
640 | */ | |
641 | int isci_task_clear_aca( | |
642 | struct domain_device *d_device, | |
643 | u8 *lun) | |
644 | { | |
645 | return TMF_RESP_FUNC_FAILED; | |
646 | } | |
647 | ||
648 | ||
649 | ||
650 | /** | |
651 | * isci_task_clear_task_set() - This function is one of the SAS Domain Template | |
652 | * functions. This is one of the Task Management functoins called by libsas. | |
653 | * @d_device: This parameter specifies the domain device associated with this | |
654 | * request. | |
655 | * @lun: This parameter specifies the lun associated with this request. | |
656 | * | |
657 | * status, zero indicates success. | |
658 | */ | |
659 | int isci_task_clear_task_set( | |
660 | struct domain_device *d_device, | |
661 | u8 *lun) | |
662 | { | |
663 | return TMF_RESP_FUNC_FAILED; | |
664 | } | |
665 | ||
666 | ||
667 | /** | |
668 | * isci_task_query_task() - This function is implemented to cause libsas to | |
669 | * correctly escalate the failed abort to a LUN or target reset (this is | |
670 | * because sas_scsi_find_task libsas function does not correctly interpret | |
671 | * all return codes from the abort task call). When TMF_RESP_FUNC_SUCC is | |
672 | * returned, libsas turns this into a LUN reset; when FUNC_FAILED is | |
673 | * returned, libsas will turn this into a target reset | |
674 | * @task: This parameter specifies the sas task being queried. | |
675 | * @lun: This parameter specifies the lun associated with this request. | |
676 | * | |
677 | * status, zero indicates success. | |
678 | */ | |
679 | int isci_task_query_task( | |
680 | struct sas_task *task) | |
681 | { | |
682 | /* See if there is a pending device reset for this device. */ | |
683 | if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET) | |
684 | return TMF_RESP_FUNC_FAILED; | |
685 | else | |
686 | return TMF_RESP_FUNC_SUCC; | |
687 | } | |
688 | ||
af5ae893 | 689 | /* |
6f231dda DW |
690 | * isci_task_request_complete() - This function is called by the sci core when |
691 | * an task request completes. | |
af5ae893 DJ |
692 | * @ihost: This parameter specifies the ISCI host object |
693 | * @ireq: This parameter is the completed isci_request object. | |
6f231dda DW |
694 | * @completion_status: This parameter specifies the completion status from the |
695 | * sci core. | |
696 | * | |
697 | * none. | |
698 | */ | |
af5ae893 DJ |
699 | void |
700 | isci_task_request_complete(struct isci_host *ihost, | |
701 | struct isci_request *ireq, | |
702 | enum sci_task_status completion_status) | |
6f231dda | 703 | { |
af5ae893 | 704 | struct isci_tmf *tmf = isci_request_access_tmf(ireq); |
b343dff1 | 705 | struct completion *tmf_complete = NULL; |
6f231dda | 706 | |
af5ae893 | 707 | dev_dbg(&ihost->pdev->dev, |
6f231dda | 708 | "%s: request = %p, status=%d\n", |
af5ae893 | 709 | __func__, ireq, completion_status); |
6f231dda | 710 | |
38d8879b | 711 | set_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags); |
6f231dda | 712 | |
b343dff1 JS |
713 | if (tmf) { |
714 | tmf->status = completion_status; | |
715 | ||
716 | if (tmf->proto == SAS_PROTOCOL_SSP) { | |
717 | memcpy(&tmf->resp.resp_iu, | |
718 | &ireq->ssp.rsp, | |
719 | SSP_RESP_IU_MAX_SIZE); | |
720 | } else if (tmf->proto == SAS_PROTOCOL_SATA) { | |
721 | memcpy(&tmf->resp.d2h_fis, | |
722 | &ireq->stp.rsp, | |
723 | sizeof(struct dev_to_host_fis)); | |
724 | } | |
725 | /* PRINT_TMF( ((struct isci_tmf *)request->task)); */ | |
726 | tmf_complete = tmf->complete; | |
6f231dda | 727 | } |
89a7301f | 728 | sci_controller_complete_io(ihost, ireq->target_device, ireq); |
67ea838d | 729 | /* set the 'terminated' flag handle to make sure it cannot be terminated |
6f231dda DW |
730 | * or completed again. |
731 | */ | |
38d8879b | 732 | set_bit(IREQ_TERMINATED, &ireq->flags); |
6f231dda | 733 | |
1db79b3e JS |
734 | if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags)) |
735 | wake_up_all(&ihost->eventq); | |
736 | ||
621120ca JS |
737 | if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags)) |
738 | isci_free_tag(ihost, ireq->io_tag); | |
b343dff1 | 739 | |
6f231dda | 740 | /* The task management part completes last. */ |
b343dff1 JS |
741 | if (tmf_complete) |
742 | complete(tmf_complete); | |
6f231dda DW |
743 | } |
744 | ||
209fae14 | 745 | static int isci_reset_device(struct isci_host *ihost, |
92776991 | 746 | struct domain_device *dev, |
bc6f387d | 747 | struct isci_remote_device *idev) |
6f231dda | 748 | { |
397497dd | 749 | int rc = TMF_RESP_FUNC_COMPLETE, reset_stat = -1; |
92776991 DW |
750 | struct sas_phy *phy = sas_get_local_phy(dev); |
751 | struct isci_port *iport = dev->port->lldd_port; | |
6f231dda | 752 | |
d06b487b | 753 | dev_dbg(&ihost->pdev->dev, "%s: idev %p\n", __func__, idev); |
6f231dda | 754 | |
14aaa9f0 JS |
755 | /* Suspend the RNC, terminate all outstanding TCs. */ |
756 | if (isci_remote_device_suspend_terminate(ihost, idev, NULL) | |
757 | != SCI_SUCCESS) { | |
f41a0c44 DW |
758 | rc = TMF_RESP_FUNC_FAILED; |
759 | goto out; | |
6f231dda | 760 | } |
14aaa9f0 JS |
761 | /* Note that since the termination for outstanding requests succeeded, |
762 | * this function will return success. This is because the resets will | |
763 | * only fail if the device has been removed (ie. hotplug), and the | |
764 | * primary duty of this function is to cleanup tasks, so that is the | |
765 | * relevant status. | |
766 | */ | |
397497dd JS |
767 | if (!test_bit(IDEV_GONE, &idev->flags)) { |
768 | if (scsi_is_sas_phy_local(phy)) { | |
769 | struct isci_phy *iphy = &ihost->phys[phy->number]; | |
770 | ||
771 | reset_stat = isci_port_perform_hard_reset(ihost, iport, | |
772 | iphy); | |
773 | } else | |
774 | reset_stat = sas_phy_reset(phy, !dev_is_sata(dev)); | |
775 | } | |
14aaa9f0 | 776 | /* Explicitly resume the RNC here, since there was no task sent. */ |
31a38ef0 | 777 | isci_remote_device_resume_from_abort(ihost, idev); |
6f231dda | 778 | |
14aaa9f0 JS |
779 | dev_dbg(&ihost->pdev->dev, "%s: idev %p complete, reset_stat=%d.\n", |
780 | __func__, idev, reset_stat); | |
f41a0c44 DW |
781 | out: |
782 | sas_put_local_phy(phy); | |
d06b487b DW |
783 | return rc; |
784 | } | |
6f231dda | 785 | |
d06b487b DW |
786 | int isci_task_I_T_nexus_reset(struct domain_device *dev) |
787 | { | |
788 | struct isci_host *ihost = dev_to_ihost(dev); | |
d06b487b DW |
789 | struct isci_remote_device *idev; |
790 | unsigned long flags; | |
bc6f387d | 791 | int ret; |
d06b487b | 792 | |
d06b487b | 793 | spin_lock_irqsave(&ihost->scic_lock, flags); |
14aaa9f0 | 794 | idev = isci_get_device(dev->lldd_dev); |
d06b487b DW |
795 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
796 | ||
5a998328 DW |
797 | if (!idev) { |
798 | /* XXX: need to cleanup any ireqs targeting this | |
799 | * domain_device | |
800 | */ | |
209fae14 DW |
801 | ret = TMF_RESP_FUNC_COMPLETE; |
802 | goto out; | |
803 | } | |
d06b487b | 804 | |
92776991 | 805 | ret = isci_reset_device(ihost, dev, idev); |
209fae14 DW |
806 | out: |
807 | isci_put_device(idev); | |
808 | return ret; | |
d06b487b | 809 | } |