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. | |
6f231dda DW |
120 | * @gfp_flags: This parameter specifies the context of this call. |
121 | * | |
122 | * status, zero indicates success. | |
123 | */ | |
79855d17 | 124 | int isci_task_execute_task(struct sas_task *task, gfp_t gfp_flags) |
6f231dda | 125 | { |
4393aa4e | 126 | struct isci_host *ihost = dev_to_ihost(task->dev); |
209fae14 | 127 | struct isci_remote_device *idev; |
6f231dda | 128 | unsigned long flags; |
79855d17 | 129 | enum sci_status status = SCI_FAILURE; |
f2088267 | 130 | bool io_ready; |
312e0c24 | 131 | u16 tag; |
6f231dda | 132 | |
79855d17 CH |
133 | spin_lock_irqsave(&ihost->scic_lock, flags); |
134 | idev = isci_lookup_device(task->dev); | |
135 | io_ready = isci_device_io_ready(idev, task); | |
136 | tag = isci_alloc_tag(ihost); | |
137 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | |
6f231dda | 138 | |
79855d17 CH |
139 | dev_dbg(&ihost->pdev->dev, |
140 | "task: %p, dev: %p idev: %p:%#lx cmd = %p\n", | |
141 | task, task->dev, idev, idev ? idev->flags : 0, | |
142 | task->uldd_task); | |
143 | ||
144 | if (!idev) { | |
145 | isci_task_refuse(ihost, task, SAS_TASK_UNDELIVERED, | |
146 | SAS_DEVICE_UNKNOWN); | |
147 | } else if (!io_ready || tag == SCI_CONTROLLER_INVALID_IO_TAG) { | |
148 | /* Indicate QUEUE_FULL so that the scsi midlayer | |
149 | * retries. | |
150 | */ | |
151 | isci_task_refuse(ihost, task, SAS_TASK_COMPLETE, | |
152 | SAS_QUEUE_FULL); | |
153 | } else { | |
154 | /* There is a device and it's ready for I/O. */ | |
155 | spin_lock_irqsave(&task->task_state_lock, flags); | |
156 | ||
157 | if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { | |
158 | /* The I/O was aborted. */ | |
159 | spin_unlock_irqrestore(&task->task_state_lock, flags); | |
160 | ||
161 | isci_task_refuse(ihost, task, | |
162 | SAS_TASK_UNDELIVERED, | |
163 | SAM_STAT_TASK_ABORTED); | |
164 | } else { | |
165 | task->task_state_flags |= SAS_TASK_AT_INITIATOR; | |
166 | spin_unlock_irqrestore(&task->task_state_lock, flags); | |
312e0c24 | 167 | |
79855d17 CH |
168 | /* build and send the request. */ |
169 | status = isci_request_execute(ihost, idev, task, tag); | |
6f231dda | 170 | |
79855d17 CH |
171 | if (status != SCI_SUCCESS) { |
172 | spin_lock_irqsave(&task->task_state_lock, flags); | |
173 | /* Did not really start this command. */ | |
174 | task->task_state_flags &= ~SAS_TASK_AT_INITIATOR; | |
6f231dda | 175 | spin_unlock_irqrestore(&task->task_state_lock, flags); |
f0846c68 | 176 | |
79855d17 CH |
177 | if (test_bit(IDEV_GONE, &idev->flags)) { |
178 | /* Indicate that the device | |
179 | * is gone. | |
180 | */ | |
181 | isci_task_refuse(ihost, task, | |
182 | SAS_TASK_UNDELIVERED, | |
183 | SAS_DEVICE_UNKNOWN); | |
184 | } else { | |
185 | /* Indicate QUEUE_FULL so that | |
186 | * the scsi midlayer retries. | |
187 | * If the request failed for | |
188 | * remote device reasons, it | |
189 | * gets returned as | |
190 | * SAS_TASK_UNDELIVERED next | |
191 | * time through. | |
192 | */ | |
193 | isci_task_refuse(ihost, task, | |
194 | SAS_TASK_COMPLETE, | |
195 | SAS_QUEUE_FULL); | |
f0846c68 | 196 | } |
6f231dda DW |
197 | } |
198 | } | |
1077a574 | 199 | } |
79855d17 CH |
200 | |
201 | if (status != SCI_SUCCESS && tag != SCI_CONTROLLER_INVALID_IO_TAG) { | |
202 | spin_lock_irqsave(&ihost->scic_lock, flags); | |
203 | /* command never hit the device, so just free | |
204 | * the tci and skip the sequence increment | |
205 | */ | |
206 | isci_tci_free(ihost, ISCI_TAG_TCI(tag)); | |
207 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | |
208 | } | |
209 | ||
210 | isci_put_device(idev); | |
6f231dda DW |
211 | return 0; |
212 | } | |
213 | ||
0d0cf14c | 214 | static struct isci_request *isci_task_request_build(struct isci_host *ihost, |
209fae14 | 215 | struct isci_remote_device *idev, |
312e0c24 | 216 | u16 tag, struct isci_tmf *isci_tmf) |
6f231dda | 217 | { |
6f231dda | 218 | enum sci_status status = SCI_FAILURE; |
0d0cf14c | 219 | struct isci_request *ireq = NULL; |
a1a113b0 | 220 | struct domain_device *dev; |
6f231dda | 221 | |
0d0cf14c | 222 | dev_dbg(&ihost->pdev->dev, |
6f231dda DW |
223 | "%s: isci_tmf = %p\n", __func__, isci_tmf); |
224 | ||
0d0cf14c | 225 | dev = idev->domain_dev; |
6f231dda DW |
226 | |
227 | /* do common allocation and init of request object. */ | |
db056250 | 228 | ireq = isci_tmf_request_from_tag(ihost, isci_tmf, tag); |
0d0cf14c DW |
229 | if (!ireq) |
230 | return NULL; | |
6f231dda DW |
231 | |
232 | /* let the core do it's construct. */ | |
89a7301f | 233 | status = sci_task_request_construct(ihost, idev, tag, |
5076a1a9 | 234 | ireq); |
6f231dda DW |
235 | |
236 | if (status != SCI_SUCCESS) { | |
0d0cf14c | 237 | dev_warn(&ihost->pdev->dev, |
89a7301f | 238 | "%s: sci_task_request_construct failed - " |
6f231dda DW |
239 | "status = 0x%x\n", |
240 | __func__, | |
241 | status); | |
db056250 | 242 | return NULL; |
6f231dda DW |
243 | } |
244 | ||
a1a113b0 | 245 | /* XXX convert to get this from task->tproto like other drivers */ |
aa9f8328 | 246 | if (dev->dev_type == SAS_END_DEVICE) { |
6f231dda | 247 | isci_tmf->proto = SAS_PROTOCOL_SSP; |
89a7301f | 248 | status = sci_task_request_construct_ssp(ireq); |
6f231dda | 249 | if (status != SCI_SUCCESS) |
db056250 | 250 | return NULL; |
6f231dda DW |
251 | } |
252 | ||
0d0cf14c | 253 | return ireq; |
6f231dda DW |
254 | } |
255 | ||
16ba7709 DW |
256 | static int isci_task_execute_tmf(struct isci_host *ihost, |
257 | struct isci_remote_device *idev, | |
258 | struct isci_tmf *tmf, unsigned long timeout_ms) | |
6f231dda DW |
259 | { |
260 | DECLARE_COMPLETION_ONSTACK(completion); | |
467e855a | 261 | enum sci_task_status status = SCI_TASK_FAILURE; |
0d0cf14c | 262 | struct isci_request *ireq; |
6f231dda DW |
263 | int ret = TMF_RESP_FUNC_FAILED; |
264 | unsigned long flags; | |
fd18388b | 265 | unsigned long timeleft; |
312e0c24 DW |
266 | u16 tag; |
267 | ||
268 | spin_lock_irqsave(&ihost->scic_lock, flags); | |
269 | tag = isci_alloc_tag(ihost); | |
270 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | |
271 | ||
272 | if (tag == SCI_CONTROLLER_INVALID_IO_TAG) | |
273 | return ret; | |
6f231dda DW |
274 | |
275 | /* sanity check, return TMF_RESP_FUNC_FAILED | |
276 | * if the device is not there and ready. | |
277 | */ | |
78a6f06e DW |
278 | if (!idev || |
279 | (!test_bit(IDEV_IO_READY, &idev->flags) && | |
280 | !test_bit(IDEV_IO_NCQERROR, &idev->flags))) { | |
0d0cf14c | 281 | dev_dbg(&ihost->pdev->dev, |
78a6f06e | 282 | "%s: idev = %p not ready (%#lx)\n", |
6f231dda | 283 | __func__, |
78a6f06e | 284 | idev, idev ? idev->flags : 0); |
312e0c24 | 285 | goto err_tci; |
6f231dda | 286 | } else |
0d0cf14c | 287 | dev_dbg(&ihost->pdev->dev, |
78a6f06e DW |
288 | "%s: idev = %p\n", |
289 | __func__, idev); | |
6f231dda DW |
290 | |
291 | /* Assign the pointer to the TMF's completion kernel wait structure. */ | |
292 | tmf->complete = &completion; | |
b343dff1 | 293 | tmf->status = SCI_FAILURE_TIMEOUT; |
6f231dda | 294 | |
78a6f06e | 295 | ireq = isci_task_request_build(ihost, idev, tag, tmf); |
312e0c24 DW |
296 | if (!ireq) |
297 | goto err_tci; | |
6f231dda | 298 | |
0d0cf14c | 299 | spin_lock_irqsave(&ihost->scic_lock, flags); |
6f231dda DW |
300 | |
301 | /* start the TMF io. */ | |
89a7301f | 302 | status = sci_controller_start_task(ihost, idev, ireq); |
6f231dda | 303 | |
467e855a | 304 | if (status != SCI_TASK_SUCCESS) { |
a8a0a133 | 305 | dev_dbg(&ihost->pdev->dev, |
6f231dda DW |
306 | "%s: start_io failed - status = 0x%x, request = %p\n", |
307 | __func__, | |
308 | status, | |
0d0cf14c DW |
309 | ireq); |
310 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | |
db056250 | 311 | goto err_tci; |
6f231dda | 312 | } |
0d0cf14c | 313 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
6f231dda | 314 | |
31a38ef0 JS |
315 | /* The RNC must be unsuspended before the TMF can get a response. */ |
316 | isci_remote_device_resume_from_abort(ihost, idev); | |
317 | ||
6f231dda | 318 | /* Wait for the TMF to complete, or a timeout. */ |
fd18388b | 319 | timeleft = wait_for_completion_timeout(&completion, |
086a0dab | 320 | msecs_to_jiffies(timeout_ms)); |
fd18388b EN |
321 | |
322 | if (timeleft == 0) { | |
b343dff1 JS |
323 | /* The TMF did not complete - this could be because |
324 | * of an unplug. Terminate the TMF request now. | |
325 | */ | |
14aaa9f0 | 326 | isci_remote_device_suspend_terminate(ihost, idev, ireq); |
fd18388b | 327 | } |
6f231dda | 328 | |
27234ab4 | 329 | isci_print_tmf(ihost, tmf); |
6f231dda DW |
330 | |
331 | if (tmf->status == SCI_SUCCESS) | |
332 | ret = TMF_RESP_FUNC_COMPLETE; | |
333 | else if (tmf->status == SCI_FAILURE_IO_RESPONSE_VALID) { | |
0d0cf14c | 334 | dev_dbg(&ihost->pdev->dev, |
6f231dda DW |
335 | "%s: tmf.status == " |
336 | "SCI_FAILURE_IO_RESPONSE_VALID\n", | |
337 | __func__); | |
338 | ret = TMF_RESP_FUNC_COMPLETE; | |
339 | } | |
340 | /* Else - leave the default "failed" status alone. */ | |
341 | ||
0d0cf14c | 342 | dev_dbg(&ihost->pdev->dev, |
6f231dda DW |
343 | "%s: completed request = %p\n", |
344 | __func__, | |
0d0cf14c | 345 | ireq); |
6f231dda | 346 | |
6f231dda | 347 | return ret; |
312e0c24 | 348 | |
312e0c24 DW |
349 | err_tci: |
350 | spin_lock_irqsave(&ihost->scic_lock, flags); | |
351 | isci_tci_free(ihost, ISCI_TAG_TCI(tag)); | |
352 | spin_unlock_irqrestore(&ihost->scic_lock, flags); | |
353 | ||
354 | return ret; | |
6f231dda DW |
355 | } |
356 | ||
16ba7709 | 357 | static void isci_task_build_tmf(struct isci_tmf *tmf, |
14aaa9f0 | 358 | enum isci_tmf_function_codes code) |
6f231dda | 359 | { |
6f231dda | 360 | memset(tmf, 0, sizeof(*tmf)); |
14aaa9f0 | 361 | tmf->tmf_code = code; |
c3f42feb | 362 | } |
1fad9e93 | 363 | |
16ba7709 DW |
364 | static void isci_task_build_abort_task_tmf(struct isci_tmf *tmf, |
365 | enum isci_tmf_function_codes code, | |
16ba7709 | 366 | struct isci_request *old_request) |
c3f42feb | 367 | { |
14aaa9f0 | 368 | isci_task_build_tmf(tmf, code); |
c3f42feb | 369 | tmf->io_tag = old_request->io_tag; |
6f231dda DW |
370 | } |
371 | ||
6f231dda DW |
372 | /** |
373 | * isci_task_send_lu_reset_sas() - This function is called by of the SAS Domain | |
374 | * Template functions. | |
375 | * @lun: This parameter specifies the lun to be reset. | |
376 | * | |
377 | * status, zero indicates success. | |
378 | */ | |
379 | static int isci_task_send_lu_reset_sas( | |
380 | struct isci_host *isci_host, | |
381 | struct isci_remote_device *isci_device, | |
382 | u8 *lun) | |
383 | { | |
384 | struct isci_tmf tmf; | |
385 | int ret = TMF_RESP_FUNC_FAILED; | |
386 | ||
387 | dev_dbg(&isci_host->pdev->dev, | |
388 | "%s: isci_host = %p, isci_device = %p\n", | |
389 | __func__, isci_host, isci_device); | |
390 | /* Send the LUN reset to the target. By the time the call returns, | |
391 | * the TMF has fully exected in the target (in which case the return | |
392 | * value is "TMF_RESP_FUNC_COMPLETE", or the request timed-out (or | |
393 | * was otherwise unable to be executed ("TMF_RESP_FUNC_FAILED"). | |
394 | */ | |
14aaa9f0 | 395 | isci_task_build_tmf(&tmf, isci_tmf_ssp_lun_reset); |
6f231dda DW |
396 | |
397 | #define ISCI_LU_RESET_TIMEOUT_MS 2000 /* 2 second timeout. */ | |
209fae14 | 398 | ret = isci_task_execute_tmf(isci_host, isci_device, &tmf, ISCI_LU_RESET_TIMEOUT_MS); |
6f231dda DW |
399 | |
400 | if (ret == TMF_RESP_FUNC_COMPLETE) | |
401 | dev_dbg(&isci_host->pdev->dev, | |
402 | "%s: %p: TMF_LU_RESET passed\n", | |
403 | __func__, isci_device); | |
404 | else | |
405 | dev_dbg(&isci_host->pdev->dev, | |
406 | "%s: %p: TMF_LU_RESET failed (%x)\n", | |
407 | __func__, isci_device, ret); | |
408 | ||
409 | return ret; | |
410 | } | |
411 | ||
43a5ab15 | 412 | int isci_task_lu_reset(struct domain_device *dev, u8 *lun) |
16ba7709 | 413 | { |
5b6bf225 | 414 | struct isci_host *ihost = dev_to_ihost(dev); |
14aaa9f0 | 415 | struct isci_remote_device *idev; |
209fae14 | 416 | unsigned long flags; |
397497dd | 417 | int ret = TMF_RESP_FUNC_COMPLETE; |
6f231dda | 418 | |
5b6bf225 | 419 | spin_lock_irqsave(&ihost->scic_lock, flags); |
63732502 | 420 | idev = isci_get_device(dev->lldd_dev); |
5b6bf225 | 421 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
6f231dda | 422 | |
5b6bf225 | 423 | dev_dbg(&ihost->pdev->dev, |
4393aa4e | 424 | "%s: domain_device=%p, isci_host=%p; isci_device=%p\n", |
14aaa9f0 | 425 | __func__, dev, ihost, idev); |
6f231dda | 426 | |
14aaa9f0 | 427 | if (!idev) { |
d80ecd57 | 428 | /* If the device is gone, escalate to I_T_Nexus_Reset. */ |
5b6bf225 | 429 | dev_dbg(&ihost->pdev->dev, "%s: No dev\n", __func__); |
6f231dda | 430 | |
d80ecd57 | 431 | ret = TMF_RESP_FUNC_FAILED; |
209fae14 | 432 | goto out; |
6f231dda DW |
433 | } |
434 | ||
08c031e4 JS |
435 | /* Suspend the RNC, kill all TCs */ |
436 | if (isci_remote_device_suspend_terminate(ihost, idev, NULL) | |
437 | != SCI_SUCCESS) { | |
438 | /* The suspend/terminate only fails if isci_get_device fails */ | |
439 | ret = TMF_RESP_FUNC_FAILED; | |
440 | goto out; | |
441 | } | |
442 | /* All pending I/Os have been terminated and cleaned up. */ | |
397497dd JS |
443 | if (!test_bit(IDEV_GONE, &idev->flags)) { |
444 | if (dev_is_sata(dev)) | |
445 | sas_ata_schedule_reset(dev); | |
446 | else | |
447 | /* Send the task management part of the reset. */ | |
448 | ret = isci_task_send_lu_reset_sas(ihost, idev, lun); | |
5b6bf225 | 449 | } |
209fae14 | 450 | out: |
14aaa9f0 | 451 | isci_put_device(idev); |
6f231dda DW |
452 | return ret; |
453 | } | |
454 | ||
455 | ||
456 | /* int (*lldd_clear_nexus_port)(struct asd_sas_port *); */ | |
457 | int isci_task_clear_nexus_port(struct asd_sas_port *port) | |
458 | { | |
459 | return TMF_RESP_FUNC_FAILED; | |
460 | } | |
461 | ||
462 | ||
463 | ||
464 | int isci_task_clear_nexus_ha(struct sas_ha_struct *ha) | |
465 | { | |
466 | return TMF_RESP_FUNC_FAILED; | |
467 | } | |
468 | ||
6f231dda DW |
469 | /* Task Management Functions. Must be called from process context. */ |
470 | ||
6f231dda DW |
471 | /** |
472 | * isci_task_abort_task() - This function is one of the SAS Domain Template | |
473 | * functions. This function is called by libsas to abort a specified task. | |
474 | * @task: This parameter specifies the SAS task to abort. | |
475 | * | |
476 | * status, zero indicates success. | |
477 | */ | |
478 | int isci_task_abort_task(struct sas_task *task) | |
479 | { | |
14aaa9f0 | 480 | struct isci_host *ihost = dev_to_ihost(task->dev); |
6f231dda | 481 | DECLARE_COMPLETION_ONSTACK(aborted_io_completion); |
a5fde225 | 482 | struct isci_request *old_request = NULL; |
14aaa9f0 | 483 | struct isci_remote_device *idev = NULL; |
a5fde225 JS |
484 | struct isci_tmf tmf; |
485 | int ret = TMF_RESP_FUNC_FAILED; | |
486 | unsigned long flags; | |
96f15f29 | 487 | int target_done_already = 0; |
6f231dda DW |
488 | |
489 | /* Get the isci_request reference from the task. Note that | |
490 | * this check does not depend on the pending request list | |
491 | * in the device, because tasks driving resets may land here | |
492 | * after completion in the core. | |
493 | */ | |
14aaa9f0 | 494 | spin_lock_irqsave(&ihost->scic_lock, flags); |
209fae14 DW |
495 | spin_lock(&task->task_state_lock); |
496 | ||
497 | old_request = task->lldd_task; | |
498 | ||
499 | /* If task is already done, the request isn't valid */ | |
500 | if (!(task->task_state_flags & SAS_TASK_STATE_DONE) && | |
501 | (task->task_state_flags & SAS_TASK_AT_INITIATOR) && | |
96f15f29 | 502 | old_request) { |
63732502 | 503 | idev = isci_get_device(task->dev->lldd_dev); |
96f15f29 JS |
504 | target_done_already = test_bit(IREQ_COMPLETE_IN_TARGET, |
505 | &old_request->flags); | |
506 | } | |
209fae14 | 507 | spin_unlock(&task->task_state_lock); |
14aaa9f0 | 508 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
6f231dda | 509 | |
14aaa9f0 | 510 | dev_warn(&ihost->pdev->dev, |
397497dd JS |
511 | "%s: dev = %p (%s%s), task = %p, old_request == %p\n", |
512 | __func__, idev, | |
513 | (dev_is_sata(task->dev) ? "STP/SATA" | |
514 | : ((dev_is_expander(task->dev)) | |
515 | ? "SMP" | |
516 | : "SSP")), | |
517 | ((idev) ? ((test_bit(IDEV_GONE, &idev->flags)) | |
518 | ? " IDEV_GONE" | |
519 | : "") | |
520 | : " <NULL>"), | |
521 | task, old_request); | |
6f231dda | 522 | |
98145cb7 JS |
523 | /* Device reset conditions signalled in task_state_flags are the |
524 | * responsbility of libsas to observe at the start of the error | |
525 | * handler thread. | |
6f231dda | 526 | */ |
14aaa9f0 | 527 | if (!idev || !old_request) { |
98145cb7 JS |
528 | /* The request has already completed and there |
529 | * is nothing to do here other than to set the task | |
530 | * done bit, and indicate that the task abort function | |
4907cb7b | 531 | * was successful. |
98145cb7 JS |
532 | */ |
533 | spin_lock_irqsave(&task->task_state_lock, flags); | |
534 | task->task_state_flags |= SAS_TASK_STATE_DONE; | |
535 | task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | | |
536 | SAS_TASK_STATE_PENDING); | |
537 | spin_unlock_irqrestore(&task->task_state_lock, flags); | |
6f231dda | 538 | |
98145cb7 | 539 | ret = TMF_RESP_FUNC_COMPLETE; |
6f231dda | 540 | |
14aaa9f0 JS |
541 | dev_warn(&ihost->pdev->dev, |
542 | "%s: abort task not needed for %p\n", | |
543 | __func__, task); | |
209fae14 | 544 | goto out; |
a5ec7f86 | 545 | } |
14aaa9f0 JS |
546 | /* Suspend the RNC, kill the TC */ |
547 | if (isci_remote_device_suspend_terminate(ihost, idev, old_request) | |
548 | != SCI_SUCCESS) { | |
549 | dev_warn(&ihost->pdev->dev, | |
550 | "%s: isci_remote_device_reset_terminate(dev=%p, " | |
551 | "req=%p, task=%p) failed\n", | |
552 | __func__, idev, old_request, task); | |
553 | ret = TMF_RESP_FUNC_FAILED; | |
209fae14 | 554 | goto out; |
6f231dda | 555 | } |
14aaa9f0 JS |
556 | spin_lock_irqsave(&ihost->scic_lock, flags); |
557 | ||
38d8879b | 558 | if (task->task_proto == SAS_PROTOCOL_SMP || |
98145cb7 | 559 | sas_protocol_ata(task->task_proto) || |
96f15f29 | 560 | target_done_already || |
397497dd | 561 | test_bit(IDEV_GONE, &idev->flags)) { |
6f231dda | 562 | |
14aaa9f0 | 563 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
6f231dda | 564 | |
31a38ef0 JS |
565 | /* No task to send, so explicitly resume the device here */ |
566 | isci_remote_device_resume_from_abort(ihost, idev); | |
567 | ||
14aaa9f0 JS |
568 | dev_warn(&ihost->pdev->dev, |
569 | "%s: %s request" | |
397497dd JS |
570 | " or complete_in_target (%d), " |
571 | "or IDEV_GONE (%d), thus no TMF\n", | |
14aaa9f0 JS |
572 | __func__, |
573 | ((task->task_proto == SAS_PROTOCOL_SMP) | |
574 | ? "SMP" | |
575 | : (sas_protocol_ata(task->task_proto) | |
576 | ? "SATA/STP" | |
577 | : "<other>") | |
578 | ), | |
579 | test_bit(IREQ_COMPLETE_IN_TARGET, | |
397497dd JS |
580 | &old_request->flags), |
581 | test_bit(IDEV_GONE, &idev->flags)); | |
5b6bf225 | 582 | |
14aaa9f0 JS |
583 | spin_lock_irqsave(&task->task_state_lock, flags); |
584 | task->task_state_flags &= ~(SAS_TASK_AT_INITIATOR | | |
585 | SAS_TASK_STATE_PENDING); | |
586 | task->task_state_flags |= SAS_TASK_STATE_DONE; | |
587 | spin_unlock_irqrestore(&task->task_state_lock, flags); | |
588 | ||
589 | ret = TMF_RESP_FUNC_COMPLETE; | |
6f231dda | 590 | } else { |
4e210519 | 591 | /* Fill in the tmf structure */ |
209fae14 | 592 | isci_task_build_abort_task_tmf(&tmf, isci_tmf_ssp_task_abort, |
c3f42feb | 593 | old_request); |
6f231dda | 594 | |
14aaa9f0 | 595 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
5b6bf225 | 596 | |
14aaa9f0 | 597 | /* Send the task management request. */ |
98145cb7 | 598 | #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* 1/2 second timeout */ |
14aaa9f0 | 599 | ret = isci_task_execute_tmf(ihost, idev, &tmf, |
6f231dda | 600 | ISCI_ABORT_TASK_TIMEOUT_MS); |
a5fde225 | 601 | } |
14aaa9f0 | 602 | out: |
63732502 JS |
603 | dev_warn(&ihost->pdev->dev, |
604 | "%s: Done; dev = %p, task = %p , old_request == %p\n", | |
605 | __func__, idev, task, old_request); | |
14aaa9f0 | 606 | isci_put_device(idev); |
6f231dda DW |
607 | return ret; |
608 | } | |
609 | ||
610 | /** | |
611 | * isci_task_abort_task_set() - This function is one of the SAS Domain Template | |
612 | * functions. This is one of the Task Management functoins called by libsas, | |
613 | * to abort all task for the given lun. | |
614 | * @d_device: This parameter specifies the domain device associated with this | |
615 | * request. | |
616 | * @lun: This parameter specifies the lun associated with this request. | |
617 | * | |
618 | * status, zero indicates success. | |
619 | */ | |
620 | int isci_task_abort_task_set( | |
621 | struct domain_device *d_device, | |
622 | u8 *lun) | |
623 | { | |
624 | return TMF_RESP_FUNC_FAILED; | |
625 | } | |
626 | ||
627 | ||
628 | /** | |
629 | * isci_task_clear_aca() - This function is one of the SAS Domain Template | |
630 | * functions. This is one of the Task Management functoins called by libsas. | |
631 | * @d_device: This parameter specifies the domain device associated with this | |
632 | * request. | |
633 | * @lun: This parameter specifies the lun associated with this request. | |
634 | * | |
635 | * status, zero indicates success. | |
636 | */ | |
637 | int isci_task_clear_aca( | |
638 | struct domain_device *d_device, | |
639 | u8 *lun) | |
640 | { | |
641 | return TMF_RESP_FUNC_FAILED; | |
642 | } | |
643 | ||
644 | ||
645 | ||
646 | /** | |
647 | * isci_task_clear_task_set() - This function is one of the SAS Domain Template | |
648 | * functions. This is one of the Task Management functoins called by libsas. | |
649 | * @d_device: This parameter specifies the domain device associated with this | |
650 | * request. | |
651 | * @lun: This parameter specifies the lun associated with this request. | |
652 | * | |
653 | * status, zero indicates success. | |
654 | */ | |
655 | int isci_task_clear_task_set( | |
656 | struct domain_device *d_device, | |
657 | u8 *lun) | |
658 | { | |
659 | return TMF_RESP_FUNC_FAILED; | |
660 | } | |
661 | ||
662 | ||
663 | /** | |
664 | * isci_task_query_task() - This function is implemented to cause libsas to | |
665 | * correctly escalate the failed abort to a LUN or target reset (this is | |
666 | * because sas_scsi_find_task libsas function does not correctly interpret | |
667 | * all return codes from the abort task call). When TMF_RESP_FUNC_SUCC is | |
668 | * returned, libsas turns this into a LUN reset; when FUNC_FAILED is | |
669 | * returned, libsas will turn this into a target reset | |
670 | * @task: This parameter specifies the sas task being queried. | |
671 | * @lun: This parameter specifies the lun associated with this request. | |
672 | * | |
673 | * status, zero indicates success. | |
674 | */ | |
675 | int isci_task_query_task( | |
676 | struct sas_task *task) | |
677 | { | |
678 | /* See if there is a pending device reset for this device. */ | |
679 | if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET) | |
680 | return TMF_RESP_FUNC_FAILED; | |
681 | else | |
682 | return TMF_RESP_FUNC_SUCC; | |
683 | } | |
684 | ||
af5ae893 | 685 | /* |
6f231dda DW |
686 | * isci_task_request_complete() - This function is called by the sci core when |
687 | * an task request completes. | |
af5ae893 DJ |
688 | * @ihost: This parameter specifies the ISCI host object |
689 | * @ireq: This parameter is the completed isci_request object. | |
6f231dda DW |
690 | * @completion_status: This parameter specifies the completion status from the |
691 | * sci core. | |
692 | * | |
693 | * none. | |
694 | */ | |
af5ae893 DJ |
695 | void |
696 | isci_task_request_complete(struct isci_host *ihost, | |
697 | struct isci_request *ireq, | |
698 | enum sci_task_status completion_status) | |
6f231dda | 699 | { |
af5ae893 | 700 | struct isci_tmf *tmf = isci_request_access_tmf(ireq); |
b343dff1 | 701 | struct completion *tmf_complete = NULL; |
6f231dda | 702 | |
af5ae893 | 703 | dev_dbg(&ihost->pdev->dev, |
6f231dda | 704 | "%s: request = %p, status=%d\n", |
af5ae893 | 705 | __func__, ireq, completion_status); |
6f231dda | 706 | |
38d8879b | 707 | set_bit(IREQ_COMPLETE_IN_TARGET, &ireq->flags); |
6f231dda | 708 | |
b343dff1 JS |
709 | if (tmf) { |
710 | tmf->status = completion_status; | |
711 | ||
712 | if (tmf->proto == SAS_PROTOCOL_SSP) { | |
713 | memcpy(&tmf->resp.resp_iu, | |
714 | &ireq->ssp.rsp, | |
715 | SSP_RESP_IU_MAX_SIZE); | |
716 | } else if (tmf->proto == SAS_PROTOCOL_SATA) { | |
717 | memcpy(&tmf->resp.d2h_fis, | |
718 | &ireq->stp.rsp, | |
719 | sizeof(struct dev_to_host_fis)); | |
720 | } | |
721 | /* PRINT_TMF( ((struct isci_tmf *)request->task)); */ | |
722 | tmf_complete = tmf->complete; | |
6f231dda | 723 | } |
89a7301f | 724 | sci_controller_complete_io(ihost, ireq->target_device, ireq); |
67ea838d | 725 | /* set the 'terminated' flag handle to make sure it cannot be terminated |
6f231dda DW |
726 | * or completed again. |
727 | */ | |
38d8879b | 728 | set_bit(IREQ_TERMINATED, &ireq->flags); |
6f231dda | 729 | |
1db79b3e JS |
730 | if (test_and_clear_bit(IREQ_ABORT_PATH_ACTIVE, &ireq->flags)) |
731 | wake_up_all(&ihost->eventq); | |
732 | ||
621120ca JS |
733 | if (!test_bit(IREQ_NO_AUTO_FREE_TAG, &ireq->flags)) |
734 | isci_free_tag(ihost, ireq->io_tag); | |
b343dff1 | 735 | |
6f231dda | 736 | /* The task management part completes last. */ |
b343dff1 JS |
737 | if (tmf_complete) |
738 | complete(tmf_complete); | |
6f231dda DW |
739 | } |
740 | ||
209fae14 | 741 | static int isci_reset_device(struct isci_host *ihost, |
92776991 | 742 | struct domain_device *dev, |
bc6f387d | 743 | struct isci_remote_device *idev) |
6f231dda | 744 | { |
397497dd | 745 | int rc = TMF_RESP_FUNC_COMPLETE, reset_stat = -1; |
92776991 DW |
746 | struct sas_phy *phy = sas_get_local_phy(dev); |
747 | struct isci_port *iport = dev->port->lldd_port; | |
6f231dda | 748 | |
d06b487b | 749 | dev_dbg(&ihost->pdev->dev, "%s: idev %p\n", __func__, idev); |
6f231dda | 750 | |
14aaa9f0 JS |
751 | /* Suspend the RNC, terminate all outstanding TCs. */ |
752 | if (isci_remote_device_suspend_terminate(ihost, idev, NULL) | |
753 | != SCI_SUCCESS) { | |
f41a0c44 DW |
754 | rc = TMF_RESP_FUNC_FAILED; |
755 | goto out; | |
6f231dda | 756 | } |
14aaa9f0 JS |
757 | /* Note that since the termination for outstanding requests succeeded, |
758 | * this function will return success. This is because the resets will | |
759 | * only fail if the device has been removed (ie. hotplug), and the | |
760 | * primary duty of this function is to cleanup tasks, so that is the | |
761 | * relevant status. | |
762 | */ | |
397497dd JS |
763 | if (!test_bit(IDEV_GONE, &idev->flags)) { |
764 | if (scsi_is_sas_phy_local(phy)) { | |
765 | struct isci_phy *iphy = &ihost->phys[phy->number]; | |
766 | ||
767 | reset_stat = isci_port_perform_hard_reset(ihost, iport, | |
768 | iphy); | |
769 | } else | |
770 | reset_stat = sas_phy_reset(phy, !dev_is_sata(dev)); | |
771 | } | |
14aaa9f0 | 772 | /* Explicitly resume the RNC here, since there was no task sent. */ |
31a38ef0 | 773 | isci_remote_device_resume_from_abort(ihost, idev); |
6f231dda | 774 | |
14aaa9f0 JS |
775 | dev_dbg(&ihost->pdev->dev, "%s: idev %p complete, reset_stat=%d.\n", |
776 | __func__, idev, reset_stat); | |
f41a0c44 DW |
777 | out: |
778 | sas_put_local_phy(phy); | |
d06b487b DW |
779 | return rc; |
780 | } | |
6f231dda | 781 | |
d06b487b DW |
782 | int isci_task_I_T_nexus_reset(struct domain_device *dev) |
783 | { | |
784 | struct isci_host *ihost = dev_to_ihost(dev); | |
d06b487b DW |
785 | struct isci_remote_device *idev; |
786 | unsigned long flags; | |
bc6f387d | 787 | int ret; |
d06b487b | 788 | |
d06b487b | 789 | spin_lock_irqsave(&ihost->scic_lock, flags); |
14aaa9f0 | 790 | idev = isci_get_device(dev->lldd_dev); |
d06b487b DW |
791 | spin_unlock_irqrestore(&ihost->scic_lock, flags); |
792 | ||
5a998328 DW |
793 | if (!idev) { |
794 | /* XXX: need to cleanup any ireqs targeting this | |
795 | * domain_device | |
796 | */ | |
ddfadd77 | 797 | ret = -ENODEV; |
209fae14 DW |
798 | goto out; |
799 | } | |
d06b487b | 800 | |
92776991 | 801 | ret = isci_reset_device(ihost, dev, idev); |
209fae14 DW |
802 | out: |
803 | isci_put_device(idev); | |
804 | return ret; | |
d06b487b | 805 | } |