diff options
Diffstat (limited to 'drivers/scsi/aic94xx')
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx_hwi.h | 3 | ||||
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx_task.c | 4 | ||||
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx_tmf.c | 246 |
3 files changed, 139 insertions, 114 deletions
diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.h b/drivers/scsi/aic94xx/aic94xx_hwi.h index 150f6706d23f..abc757559c1a 100644 --- a/drivers/scsi/aic94xx/aic94xx_hwi.h +++ b/drivers/scsi/aic94xx/aic94xx_hwi.h | |||
@@ -140,7 +140,7 @@ struct asd_ascb { | |||
140 | 140 | ||
141 | /* internally generated command */ | 141 | /* internally generated command */ |
142 | struct timer_list timer; | 142 | struct timer_list timer; |
143 | struct completion completion; | 143 | struct completion *completion; |
144 | u8 tag_valid:1; | 144 | u8 tag_valid:1; |
145 | __be16 tag; /* error recovery only */ | 145 | __be16 tag; /* error recovery only */ |
146 | 146 | ||
@@ -294,7 +294,6 @@ static inline void asd_init_ascb(struct asd_ha_struct *asd_ha, | |||
294 | ascb->timer.function = NULL; | 294 | ascb->timer.function = NULL; |
295 | init_timer(&ascb->timer); | 295 | init_timer(&ascb->timer); |
296 | ascb->tc_index = -1; | 296 | ascb->tc_index = -1; |
297 | init_completion(&ascb->completion); | ||
298 | } | 297 | } |
299 | 298 | ||
300 | /* Must be called with the tc_index_lock held! | 299 | /* Must be called with the tc_index_lock held! |
diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c index 965d4bb999d9..008df9ab92a5 100644 --- a/drivers/scsi/aic94xx/aic94xx_task.c +++ b/drivers/scsi/aic94xx/aic94xx_task.c | |||
@@ -343,11 +343,13 @@ Again: | |||
343 | task->task_state_flags &= ~SAS_TASK_AT_INITIATOR; | 343 | task->task_state_flags &= ~SAS_TASK_AT_INITIATOR; |
344 | task->task_state_flags |= SAS_TASK_STATE_DONE; | 344 | task->task_state_flags |= SAS_TASK_STATE_DONE; |
345 | if (unlikely((task->task_state_flags & SAS_TASK_STATE_ABORTED))) { | 345 | if (unlikely((task->task_state_flags & SAS_TASK_STATE_ABORTED))) { |
346 | struct completion *completion = ascb->completion; | ||
346 | spin_unlock_irqrestore(&task->task_state_lock, flags); | 347 | spin_unlock_irqrestore(&task->task_state_lock, flags); |
347 | ASD_DPRINTK("task 0x%p done with opcode 0x%x resp 0x%x " | 348 | ASD_DPRINTK("task 0x%p done with opcode 0x%x resp 0x%x " |
348 | "stat 0x%x but aborted by upper layer!\n", | 349 | "stat 0x%x but aborted by upper layer!\n", |
349 | task, opcode, ts->resp, ts->stat); | 350 | task, opcode, ts->resp, ts->stat); |
350 | complete(&ascb->completion); | 351 | if (completion) |
352 | complete(completion); | ||
351 | } else { | 353 | } else { |
352 | spin_unlock_irqrestore(&task->task_state_lock, flags); | 354 | spin_unlock_irqrestore(&task->task_state_lock, flags); |
353 | task->lldd_task = NULL; | 355 | task->lldd_task = NULL; |
diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c index d684c7432e63..b9ac8f703a1d 100644 --- a/drivers/scsi/aic94xx/aic94xx_tmf.c +++ b/drivers/scsi/aic94xx/aic94xx_tmf.c | |||
@@ -53,50 +53,64 @@ static int asd_enqueue_internal(struct asd_ascb *ascb, | |||
53 | return res; | 53 | return res; |
54 | } | 54 | } |
55 | 55 | ||
56 | static inline void asd_timedout_common(unsigned long data) | 56 | /* ---------- CLEAR NEXUS ---------- */ |
57 | { | ||
58 | struct asd_ascb *ascb = (void *) data; | ||
59 | struct asd_seq_data *seq = &ascb->ha->seq; | ||
60 | unsigned long flags; | ||
61 | 57 | ||
62 | spin_lock_irqsave(&seq->pend_q_lock, flags); | 58 | struct tasklet_completion_status { |
63 | seq->pending--; | 59 | int dl_opcode; |
64 | list_del_init(&ascb->list); | 60 | int tmf_state; |
65 | spin_unlock_irqrestore(&seq->pend_q_lock, flags); | 61 | u8 tag_valid:1; |
66 | } | 62 | __be16 tag; |
63 | }; | ||
64 | |||
65 | #define DECLARE_TCS(tcs) \ | ||
66 | struct tasklet_completion_status tcs = { \ | ||
67 | .dl_opcode = 0, \ | ||
68 | .tmf_state = 0, \ | ||
69 | .tag_valid = 0, \ | ||
70 | .tag = 0, \ | ||
71 | } | ||
67 | 72 | ||
68 | /* ---------- CLEAR NEXUS ---------- */ | ||
69 | 73 | ||
70 | static void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb, | 74 | static void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb, |
71 | struct done_list_struct *dl) | 75 | struct done_list_struct *dl) |
72 | { | 76 | { |
77 | struct tasklet_completion_status *tcs = ascb->uldd_task; | ||
73 | ASD_DPRINTK("%s: here\n", __FUNCTION__); | 78 | ASD_DPRINTK("%s: here\n", __FUNCTION__); |
74 | if (!del_timer(&ascb->timer)) { | 79 | if (!del_timer(&ascb->timer)) { |
75 | ASD_DPRINTK("%s: couldn't delete timer\n", __FUNCTION__); | 80 | ASD_DPRINTK("%s: couldn't delete timer\n", __FUNCTION__); |
76 | return; | 81 | return; |
77 | } | 82 | } |
78 | ASD_DPRINTK("%s: opcode: 0x%x\n", __FUNCTION__, dl->opcode); | 83 | ASD_DPRINTK("%s: opcode: 0x%x\n", __FUNCTION__, dl->opcode); |
79 | ascb->uldd_task = (void *) (unsigned long) dl->opcode; | 84 | tcs->dl_opcode = dl->opcode; |
80 | complete(&ascb->completion); | 85 | complete(ascb->completion); |
86 | asd_ascb_free(ascb); | ||
81 | } | 87 | } |
82 | 88 | ||
83 | static void asd_clear_nexus_timedout(unsigned long data) | 89 | static void asd_clear_nexus_timedout(unsigned long data) |
84 | { | 90 | { |
85 | struct asd_ascb *ascb = (void *) data; | 91 | struct asd_ascb *ascb = (void *)data; |
92 | struct tasklet_completion_status *tcs = ascb->uldd_task; | ||
86 | 93 | ||
87 | ASD_DPRINTK("%s: here\n", __FUNCTION__); | 94 | ASD_DPRINTK("%s: here\n", __FUNCTION__); |
88 | asd_timedout_common(data); | 95 | tcs->dl_opcode = TMF_RESP_FUNC_FAILED; |
89 | ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED; | 96 | complete(ascb->completion); |
90 | complete(&ascb->completion); | ||
91 | } | 97 | } |
92 | 98 | ||
93 | #define CLEAR_NEXUS_PRE \ | 99 | #define CLEAR_NEXUS_PRE \ |
100 | struct asd_ascb *ascb; \ | ||
101 | struct scb *scb; \ | ||
102 | int res; \ | ||
103 | DECLARE_COMPLETION_ONSTACK(completion); \ | ||
104 | DECLARE_TCS(tcs); \ | ||
105 | \ | ||
94 | ASD_DPRINTK("%s: PRE\n", __FUNCTION__); \ | 106 | ASD_DPRINTK("%s: PRE\n", __FUNCTION__); \ |
95 | res = 1; \ | 107 | res = 1; \ |
96 | ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); \ | 108 | ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); \ |
97 | if (!ascb) \ | 109 | if (!ascb) \ |
98 | return -ENOMEM; \ | 110 | return -ENOMEM; \ |
99 | \ | 111 | \ |
112 | ascb->completion = &completion; \ | ||
113 | ascb->uldd_task = &tcs; \ | ||
100 | scb = ascb->scb; \ | 114 | scb = ascb->scb; \ |
101 | scb->header.opcode = CLEAR_NEXUS | 115 | scb->header.opcode = CLEAR_NEXUS |
102 | 116 | ||
@@ -107,10 +121,11 @@ static void asd_clear_nexus_timedout(unsigned long data) | |||
107 | if (res) \ | 121 | if (res) \ |
108 | goto out_err; \ | 122 | goto out_err; \ |
109 | ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __FUNCTION__); \ | 123 | ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __FUNCTION__); \ |
110 | wait_for_completion(&ascb->completion); \ | 124 | wait_for_completion(&completion); \ |
111 | res = (int) (unsigned long) ascb->uldd_task; \ | 125 | res = tcs.dl_opcode; \ |
112 | if (res == TC_NO_ERROR) \ | 126 | if (res == TC_NO_ERROR) \ |
113 | res = TMF_RESP_FUNC_COMPLETE; \ | 127 | res = TMF_RESP_FUNC_COMPLETE; \ |
128 | return res; \ | ||
114 | out_err: \ | 129 | out_err: \ |
115 | asd_ascb_free(ascb); \ | 130 | asd_ascb_free(ascb); \ |
116 | return res | 131 | return res |
@@ -118,9 +133,6 @@ out_err: \ | |||
118 | int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha) | 133 | int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha) |
119 | { | 134 | { |
120 | struct asd_ha_struct *asd_ha = sas_ha->lldd_ha; | 135 | struct asd_ha_struct *asd_ha = sas_ha->lldd_ha; |
121 | struct asd_ascb *ascb; | ||
122 | struct scb *scb; | ||
123 | int res; | ||
124 | 136 | ||
125 | CLEAR_NEXUS_PRE; | 137 | CLEAR_NEXUS_PRE; |
126 | scb->clear_nexus.nexus = NEXUS_ADAPTER; | 138 | scb->clear_nexus.nexus = NEXUS_ADAPTER; |
@@ -130,9 +142,6 @@ int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha) | |||
130 | int asd_clear_nexus_port(struct asd_sas_port *port) | 142 | int asd_clear_nexus_port(struct asd_sas_port *port) |
131 | { | 143 | { |
132 | struct asd_ha_struct *asd_ha = port->ha->lldd_ha; | 144 | struct asd_ha_struct *asd_ha = port->ha->lldd_ha; |
133 | struct asd_ascb *ascb; | ||
134 | struct scb *scb; | ||
135 | int res; | ||
136 | 145 | ||
137 | CLEAR_NEXUS_PRE; | 146 | CLEAR_NEXUS_PRE; |
138 | scb->clear_nexus.nexus = NEXUS_PORT; | 147 | scb->clear_nexus.nexus = NEXUS_PORT; |
@@ -150,9 +159,6 @@ static int asd_clear_nexus_I_T(struct domain_device *dev, | |||
150 | enum clear_nexus_phase phase) | 159 | enum clear_nexus_phase phase) |
151 | { | 160 | { |
152 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; | 161 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; |
153 | struct asd_ascb *ascb; | ||
154 | struct scb *scb; | ||
155 | int res; | ||
156 | 162 | ||
157 | CLEAR_NEXUS_PRE; | 163 | CLEAR_NEXUS_PRE; |
158 | scb->clear_nexus.nexus = NEXUS_I_T; | 164 | scb->clear_nexus.nexus = NEXUS_I_T; |
@@ -210,9 +216,6 @@ int asd_I_T_nexus_reset(struct domain_device *dev) | |||
210 | static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun) | 216 | static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun) |
211 | { | 217 | { |
212 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; | 218 | struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; |
213 | struct asd_ascb *ascb; | ||
214 | struct scb *scb; | ||
215 | int res; | ||
216 | 219 | ||
217 | CLEAR_NEXUS_PRE; | 220 | CLEAR_NEXUS_PRE; |
218 | scb->clear_nexus.nexus = NEXUS_I_T_L; | 221 | scb->clear_nexus.nexus = NEXUS_I_T_L; |
@@ -227,9 +230,6 @@ static int asd_clear_nexus_tag(struct sas_task *task) | |||
227 | { | 230 | { |
228 | struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha; | 231 | struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha; |
229 | struct asd_ascb *tascb = task->lldd_task; | 232 | struct asd_ascb *tascb = task->lldd_task; |
230 | struct asd_ascb *ascb; | ||
231 | struct scb *scb; | ||
232 | int res; | ||
233 | 233 | ||
234 | CLEAR_NEXUS_PRE; | 234 | CLEAR_NEXUS_PRE; |
235 | scb->clear_nexus.nexus = NEXUS_TAG; | 235 | scb->clear_nexus.nexus = NEXUS_TAG; |
@@ -245,9 +245,6 @@ static int asd_clear_nexus_index(struct sas_task *task) | |||
245 | { | 245 | { |
246 | struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha; | 246 | struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha; |
247 | struct asd_ascb *tascb = task->lldd_task; | 247 | struct asd_ascb *tascb = task->lldd_task; |
248 | struct asd_ascb *ascb; | ||
249 | struct scb *scb; | ||
250 | int res; | ||
251 | 248 | ||
252 | CLEAR_NEXUS_PRE; | 249 | CLEAR_NEXUS_PRE; |
253 | scb->clear_nexus.nexus = NEXUS_TRANS_CX; | 250 | scb->clear_nexus.nexus = NEXUS_TRANS_CX; |
@@ -263,11 +260,11 @@ static int asd_clear_nexus_index(struct sas_task *task) | |||
263 | static void asd_tmf_timedout(unsigned long data) | 260 | static void asd_tmf_timedout(unsigned long data) |
264 | { | 261 | { |
265 | struct asd_ascb *ascb = (void *) data; | 262 | struct asd_ascb *ascb = (void *) data; |
263 | struct tasklet_completion_status *tcs = ascb->uldd_task; | ||
266 | 264 | ||
267 | ASD_DPRINTK("tmf timed out\n"); | 265 | ASD_DPRINTK("tmf timed out\n"); |
268 | asd_timedout_common(data); | 266 | tcs->tmf_state = TMF_RESP_FUNC_FAILED; |
269 | ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED; | 267 | complete(ascb->completion); |
270 | complete(&ascb->completion); | ||
271 | } | 268 | } |
272 | 269 | ||
273 | static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb, | 270 | static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb, |
@@ -319,18 +316,24 @@ static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb, | |||
319 | static void asd_tmf_tasklet_complete(struct asd_ascb *ascb, | 316 | static void asd_tmf_tasklet_complete(struct asd_ascb *ascb, |
320 | struct done_list_struct *dl) | 317 | struct done_list_struct *dl) |
321 | { | 318 | { |
319 | struct tasklet_completion_status *tcs; | ||
320 | |||
322 | if (!del_timer(&ascb->timer)) | 321 | if (!del_timer(&ascb->timer)) |
323 | return; | 322 | return; |
324 | 323 | ||
324 | tcs = ascb->uldd_task; | ||
325 | ASD_DPRINTK("tmf tasklet complete\n"); | 325 | ASD_DPRINTK("tmf tasklet complete\n"); |
326 | 326 | ||
327 | if (dl->opcode == TC_SSP_RESP) | 327 | tcs->dl_opcode = dl->opcode; |
328 | ascb->uldd_task = (void *) (unsigned long) | ||
329 | asd_get_tmf_resp_tasklet(ascb, dl); | ||
330 | else | ||
331 | ascb->uldd_task = (void *) 0xFF00 + (unsigned long) dl->opcode; | ||
332 | 328 | ||
333 | complete(&ascb->completion); | 329 | if (dl->opcode == TC_SSP_RESP) { |
330 | tcs->tmf_state = asd_get_tmf_resp_tasklet(ascb, dl); | ||
331 | tcs->tag_valid = ascb->tag_valid; | ||
332 | tcs->tag = ascb->tag; | ||
333 | } | ||
334 | |||
335 | complete(ascb->completion); | ||
336 | asd_ascb_free(ascb); | ||
334 | } | 337 | } |
335 | 338 | ||
336 | static inline int asd_clear_nexus(struct sas_task *task) | 339 | static inline int asd_clear_nexus(struct sas_task *task) |
@@ -338,15 +341,19 @@ static inline int asd_clear_nexus(struct sas_task *task) | |||
338 | int res = TMF_RESP_FUNC_FAILED; | 341 | int res = TMF_RESP_FUNC_FAILED; |
339 | int leftover; | 342 | int leftover; |
340 | struct asd_ascb *tascb = task->lldd_task; | 343 | struct asd_ascb *tascb = task->lldd_task; |
344 | DECLARE_COMPLETION_ONSTACK(completion); | ||
341 | unsigned long flags; | 345 | unsigned long flags; |
342 | 346 | ||
347 | tascb->completion = &completion; | ||
348 | |||
343 | ASD_DPRINTK("task not done, clearing nexus\n"); | 349 | ASD_DPRINTK("task not done, clearing nexus\n"); |
344 | if (tascb->tag_valid) | 350 | if (tascb->tag_valid) |
345 | res = asd_clear_nexus_tag(task); | 351 | res = asd_clear_nexus_tag(task); |
346 | else | 352 | else |
347 | res = asd_clear_nexus_index(task); | 353 | res = asd_clear_nexus_index(task); |
348 | leftover = wait_for_completion_timeout(&tascb->completion, | 354 | leftover = wait_for_completion_timeout(&completion, |
349 | AIC94XX_SCB_TIMEOUT); | 355 | AIC94XX_SCB_TIMEOUT); |
356 | tascb->completion = NULL; | ||
350 | ASD_DPRINTK("came back from clear nexus\n"); | 357 | ASD_DPRINTK("came back from clear nexus\n"); |
351 | spin_lock_irqsave(&task->task_state_lock, flags); | 358 | spin_lock_irqsave(&task->task_state_lock, flags); |
352 | if (leftover < 1) | 359 | if (leftover < 1) |
@@ -400,6 +407,11 @@ int asd_abort_task(struct sas_task *task) | |||
400 | struct asd_ascb *ascb = NULL; | 407 | struct asd_ascb *ascb = NULL; |
401 | struct scb *scb; | 408 | struct scb *scb; |
402 | int leftover; | 409 | int leftover; |
410 | DECLARE_TCS(tcs); | ||
411 | DECLARE_COMPLETION_ONSTACK(completion); | ||
412 | DECLARE_COMPLETION_ONSTACK(tascb_completion); | ||
413 | |||
414 | tascb->completion = &tascb_completion; | ||
403 | 415 | ||
404 | spin_lock_irqsave(&task->task_state_lock, flags); | 416 | spin_lock_irqsave(&task->task_state_lock, flags); |
405 | if (task->task_state_flags & SAS_TASK_STATE_DONE) { | 417 | if (task->task_state_flags & SAS_TASK_STATE_DONE) { |
@@ -413,8 +425,10 @@ int asd_abort_task(struct sas_task *task) | |||
413 | ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); | 425 | ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); |
414 | if (!ascb) | 426 | if (!ascb) |
415 | return -ENOMEM; | 427 | return -ENOMEM; |
416 | scb = ascb->scb; | ||
417 | 428 | ||
429 | ascb->uldd_task = &tcs; | ||
430 | ascb->completion = &completion; | ||
431 | scb = ascb->scb; | ||
418 | scb->header.opcode = SCB_ABORT_TASK; | 432 | scb->header.opcode = SCB_ABORT_TASK; |
419 | 433 | ||
420 | switch (task->task_proto) { | 434 | switch (task->task_proto) { |
@@ -456,13 +470,12 @@ int asd_abort_task(struct sas_task *task) | |||
456 | res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete, | 470 | res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete, |
457 | asd_tmf_timedout); | 471 | asd_tmf_timedout); |
458 | if (res) | 472 | if (res) |
459 | goto out; | 473 | goto out_free; |
460 | wait_for_completion(&ascb->completion); | 474 | wait_for_completion(&completion); |
461 | ASD_DPRINTK("tmf came back\n"); | 475 | ASD_DPRINTK("tmf came back\n"); |
462 | 476 | ||
463 | res = (int) (unsigned long) ascb->uldd_task; | 477 | tascb->tag = tcs.tag; |
464 | tascb->tag = ascb->tag; | 478 | tascb->tag_valid = tcs.tag_valid; |
465 | tascb->tag_valid = ascb->tag_valid; | ||
466 | 479 | ||
467 | spin_lock_irqsave(&task->task_state_lock, flags); | 480 | spin_lock_irqsave(&task->task_state_lock, flags); |
468 | if (task->task_state_flags & SAS_TASK_STATE_DONE) { | 481 | if (task->task_state_flags & SAS_TASK_STATE_DONE) { |
@@ -473,63 +486,68 @@ int asd_abort_task(struct sas_task *task) | |||
473 | } | 486 | } |
474 | spin_unlock_irqrestore(&task->task_state_lock, flags); | 487 | spin_unlock_irqrestore(&task->task_state_lock, flags); |
475 | 488 | ||
476 | switch (res) { | 489 | if (tcs.dl_opcode == TC_SSP_RESP) { |
477 | /* The task to be aborted has been sent to the device. | 490 | /* The task to be aborted has been sent to the device. |
478 | * We got a Response IU for the ABORT TASK TMF. */ | 491 | * We got a Response IU for the ABORT TASK TMF. */ |
479 | case TC_NO_ERROR + 0xFF00: | 492 | if (tcs.tmf_state == TMF_RESP_FUNC_COMPLETE) |
480 | case TMF_RESP_FUNC_COMPLETE: | 493 | res = asd_clear_nexus(task); |
481 | case TMF_RESP_FUNC_FAILED: | 494 | else |
482 | res = asd_clear_nexus(task); | 495 | res = tcs.tmf_state; |
483 | break; | 496 | } else if (tcs.dl_opcode == TC_NO_ERROR && |
484 | case TMF_RESP_INVALID_FRAME: | 497 | tcs.tmf_state == TMF_RESP_FUNC_FAILED) { |
485 | case TMF_RESP_OVERLAPPED_TAG: | 498 | /* timeout */ |
486 | case TMF_RESP_FUNC_ESUPP: | ||
487 | case TMF_RESP_NO_LUN: | ||
488 | goto out_done; break; | ||
489 | } | ||
490 | /* In the following we assume that the managing layer | ||
491 | * will _never_ make a mistake, when issuing ABORT TASK. | ||
492 | */ | ||
493 | switch (res) { | ||
494 | default: | ||
495 | res = asd_clear_nexus(task); | ||
496 | /* fallthrough */ | ||
497 | case TC_NO_ERROR + 0xFF00: | ||
498 | case TMF_RESP_FUNC_COMPLETE: | ||
499 | break; | ||
500 | /* The task hasn't been sent to the device xor we never got | ||
501 | * a (sane) Response IU for the ABORT TASK TMF. | ||
502 | */ | ||
503 | case TF_NAK_RECV + 0xFF00: | ||
504 | res = TMF_RESP_INVALID_FRAME; | ||
505 | break; | ||
506 | case TF_TMF_TASK_DONE + 0xFF00: /* done but not reported yet */ | ||
507 | res = TMF_RESP_FUNC_FAILED; | 499 | res = TMF_RESP_FUNC_FAILED; |
508 | leftover = wait_for_completion_timeout(&tascb->completion, | 500 | } else { |
509 | AIC94XX_SCB_TIMEOUT); | 501 | /* In the following we assume that the managing layer |
510 | spin_lock_irqsave(&task->task_state_lock, flags); | 502 | * will _never_ make a mistake, when issuing ABORT |
511 | if (leftover < 1) | 503 | * TASK. |
504 | */ | ||
505 | switch (tcs.dl_opcode) { | ||
506 | default: | ||
507 | res = asd_clear_nexus(task); | ||
508 | /* fallthrough */ | ||
509 | case TC_NO_ERROR: | ||
510 | break; | ||
511 | /* The task hasn't been sent to the device xor | ||
512 | * we never got a (sane) Response IU for the | ||
513 | * ABORT TASK TMF. | ||
514 | */ | ||
515 | case TF_NAK_RECV: | ||
516 | res = TMF_RESP_INVALID_FRAME; | ||
517 | break; | ||
518 | case TF_TMF_TASK_DONE: /* done but not reported yet */ | ||
512 | res = TMF_RESP_FUNC_FAILED; | 519 | res = TMF_RESP_FUNC_FAILED; |
513 | if (task->task_state_flags & SAS_TASK_STATE_DONE) | 520 | leftover = |
521 | wait_for_completion_timeout(&tascb_completion, | ||
522 | AIC94XX_SCB_TIMEOUT); | ||
523 | spin_lock_irqsave(&task->task_state_lock, flags); | ||
524 | if (leftover < 1) | ||
525 | res = TMF_RESP_FUNC_FAILED; | ||
526 | if (task->task_state_flags & SAS_TASK_STATE_DONE) | ||
527 | res = TMF_RESP_FUNC_COMPLETE; | ||
528 | spin_unlock_irqrestore(&task->task_state_lock, flags); | ||
529 | break; | ||
530 | case TF_TMF_NO_TAG: | ||
531 | case TF_TMF_TAG_FREE: /* the tag is in the free list */ | ||
532 | case TF_TMF_NO_CONN_HANDLE: /* no such device */ | ||
514 | res = TMF_RESP_FUNC_COMPLETE; | 533 | res = TMF_RESP_FUNC_COMPLETE; |
515 | spin_unlock_irqrestore(&task->task_state_lock, flags); | 534 | break; |
516 | goto out_done; | 535 | case TF_TMF_NO_CTX: /* not in seq, or proto != SSP */ |
517 | case TF_TMF_NO_TAG + 0xFF00: | 536 | res = TMF_RESP_FUNC_ESUPP; |
518 | case TF_TMF_TAG_FREE + 0xFF00: /* the tag is in the free list */ | 537 | break; |
519 | case TF_TMF_NO_CONN_HANDLE + 0xFF00: /* no such device */ | 538 | } |
520 | res = TMF_RESP_FUNC_COMPLETE; | ||
521 | goto out_done; | ||
522 | case TF_TMF_NO_CTX + 0xFF00: /* not in seq, or proto != SSP */ | ||
523 | res = TMF_RESP_FUNC_ESUPP; | ||
524 | goto out; | ||
525 | } | 539 | } |
526 | out_done: | 540 | out_done: |
541 | tascb->completion = NULL; | ||
527 | if (res == TMF_RESP_FUNC_COMPLETE) { | 542 | if (res == TMF_RESP_FUNC_COMPLETE) { |
528 | task->lldd_task = NULL; | 543 | task->lldd_task = NULL; |
529 | mb(); | 544 | mb(); |
530 | asd_ascb_free(tascb); | 545 | asd_ascb_free(tascb); |
531 | } | 546 | } |
532 | out: | 547 | ASD_DPRINTK("task 0x%p aborted, res: 0x%x\n", task, res); |
548 | return res; | ||
549 | |||
550 | out_free: | ||
533 | asd_ascb_free(ascb); | 551 | asd_ascb_free(ascb); |
534 | ASD_DPRINTK("task 0x%p aborted, res: 0x%x\n", task, res); | 552 | ASD_DPRINTK("task 0x%p aborted, res: 0x%x\n", task, res); |
535 | return res; | 553 | return res; |
@@ -557,6 +575,8 @@ static int asd_initiate_ssp_tmf(struct domain_device *dev, u8 *lun, | |||
557 | struct asd_ascb *ascb; | 575 | struct asd_ascb *ascb; |
558 | int res = 1; | 576 | int res = 1; |
559 | struct scb *scb; | 577 | struct scb *scb; |
578 | DECLARE_COMPLETION_ONSTACK(completion); | ||
579 | DECLARE_TCS(tcs); | ||
560 | 580 | ||
561 | if (!(dev->tproto & SAS_PROTOCOL_SSP)) | 581 | if (!(dev->tproto & SAS_PROTOCOL_SSP)) |
562 | return TMF_RESP_FUNC_ESUPP; | 582 | return TMF_RESP_FUNC_ESUPP; |
@@ -564,6 +584,9 @@ static int asd_initiate_ssp_tmf(struct domain_device *dev, u8 *lun, | |||
564 | ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); | 584 | ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); |
565 | if (!ascb) | 585 | if (!ascb) |
566 | return -ENOMEM; | 586 | return -ENOMEM; |
587 | |||
588 | ascb->completion = &completion; | ||
589 | ascb->uldd_task = &tcs; | ||
567 | scb = ascb->scb; | 590 | scb = ascb->scb; |
568 | 591 | ||
569 | if (tmf == TMF_QUERY_TASK) | 592 | if (tmf == TMF_QUERY_TASK) |
@@ -596,31 +619,32 @@ static int asd_initiate_ssp_tmf(struct domain_device *dev, u8 *lun, | |||
596 | asd_tmf_timedout); | 619 | asd_tmf_timedout); |
597 | if (res) | 620 | if (res) |
598 | goto out_err; | 621 | goto out_err; |
599 | wait_for_completion(&ascb->completion); | 622 | wait_for_completion(&completion); |
600 | res = (int) (unsigned long) ascb->uldd_task; | ||
601 | 623 | ||
602 | switch (res) { | 624 | switch (tcs.dl_opcode) { |
603 | case TC_NO_ERROR + 0xFF00: | 625 | case TC_NO_ERROR: |
604 | res = TMF_RESP_FUNC_COMPLETE; | 626 | res = TMF_RESP_FUNC_COMPLETE; |
605 | break; | 627 | break; |
606 | case TF_NAK_RECV + 0xFF00: | 628 | case TF_NAK_RECV: |
607 | res = TMF_RESP_INVALID_FRAME; | 629 | res = TMF_RESP_INVALID_FRAME; |
608 | break; | 630 | break; |
609 | case TF_TMF_TASK_DONE + 0xFF00: | 631 | case TF_TMF_TASK_DONE: |
610 | res = TMF_RESP_FUNC_FAILED; | 632 | res = TMF_RESP_FUNC_FAILED; |
611 | break; | 633 | break; |
612 | case TF_TMF_NO_TAG + 0xFF00: | 634 | case TF_TMF_NO_TAG: |
613 | case TF_TMF_TAG_FREE + 0xFF00: /* the tag is in the free list */ | 635 | case TF_TMF_TAG_FREE: /* the tag is in the free list */ |
614 | case TF_TMF_NO_CONN_HANDLE + 0xFF00: /* no such device */ | 636 | case TF_TMF_NO_CONN_HANDLE: /* no such device */ |
615 | res = TMF_RESP_FUNC_COMPLETE; | 637 | res = TMF_RESP_FUNC_COMPLETE; |
616 | break; | 638 | break; |
617 | case TF_TMF_NO_CTX + 0xFF00: /* not in seq, or proto != SSP */ | 639 | case TF_TMF_NO_CTX: /* not in seq, or proto != SSP */ |
618 | res = TMF_RESP_FUNC_ESUPP; | 640 | res = TMF_RESP_FUNC_ESUPP; |
619 | break; | 641 | break; |
620 | default: | 642 | default: |
621 | /* Allow TMF response codes to propagate upwards */ | 643 | /* Allow TMF response codes to propagate upwards */ |
644 | res = tcs.dl_opcode; | ||
622 | break; | 645 | break; |
623 | } | 646 | } |
647 | return res; | ||
624 | out_err: | 648 | out_err: |
625 | asd_ascb_free(ascb); | 649 | asd_ascb_free(ascb); |
626 | return res; | 650 | return res; |