diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2012-06-19 03:54:54 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-06-25 14:51:01 -0400 |
commit | 023b515e5b304122f3802abaa68d1da46fdf48b8 (patch) | |
tree | a56225ce211baf8216c24c2fc00f21c75266e419 | |
parent | bdd000fb34202530e5cd11260d06f57e2daf63c9 (diff) |
uas: task mgmt & error handling
Add task management support, wind up in abort and device reset error
handlers. Cancel all in-flight urbs in bus reset handler.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/usb/storage/uas.c | 160 | ||||
-rw-r--r-- | include/linux/usb/uas.h | 40 |
2 files changed, 171 insertions, 29 deletions
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 875829a56183..638cd64f9610 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c | |||
@@ -43,7 +43,8 @@ struct uas_dev_info { | |||
43 | struct usb_device *udev; | 43 | struct usb_device *udev; |
44 | struct usb_anchor sense_urbs; | 44 | struct usb_anchor sense_urbs; |
45 | struct usb_anchor data_urbs; | 45 | struct usb_anchor data_urbs; |
46 | int qdepth; | 46 | int qdepth, resetting; |
47 | struct response_ui response; | ||
47 | unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe; | 48 | unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe; |
48 | unsigned use_streams:1; | 49 | unsigned use_streams:1; |
49 | unsigned uas_sense_old:1; | 50 | unsigned uas_sense_old:1; |
@@ -68,6 +69,7 @@ enum { | |||
68 | struct uas_cmd_info { | 69 | struct uas_cmd_info { |
69 | unsigned int state; | 70 | unsigned int state; |
70 | unsigned int stream; | 71 | unsigned int stream; |
72 | unsigned int aborted; | ||
71 | struct urb *cmd_urb; | 73 | struct urb *cmd_urb; |
72 | struct urb *data_in_urb; | 74 | struct urb *data_in_urb; |
73 | struct urb *data_out_urb; | 75 | struct urb *data_out_urb; |
@@ -222,16 +224,24 @@ static void uas_stat_cmplt(struct urb *urb) | |||
222 | return; | 224 | return; |
223 | } | 225 | } |
224 | 226 | ||
227 | if (devinfo->resetting) { | ||
228 | usb_free_urb(urb); | ||
229 | return; | ||
230 | } | ||
231 | |||
225 | tag = be16_to_cpup(&iu->tag) - 1; | 232 | tag = be16_to_cpup(&iu->tag) - 1; |
226 | if (tag == 0) | 233 | if (tag == 0) |
227 | cmnd = devinfo->cmnd; | 234 | cmnd = devinfo->cmnd; |
228 | else | 235 | else |
229 | cmnd = scsi_host_find_tag(shost, tag - 1); | 236 | cmnd = scsi_host_find_tag(shost, tag - 1); |
230 | if (!cmnd) { | 237 | if (!cmnd) { |
231 | usb_free_urb(urb); | 238 | if (iu->iu_id != IU_ID_RESPONSE) { |
232 | return; | 239 | usb_free_urb(urb); |
240 | return; | ||
241 | } | ||
242 | } else { | ||
243 | cmdinfo = (void *)&cmnd->SCp; | ||
233 | } | 244 | } |
234 | cmdinfo = (void *)&cmnd->SCp; | ||
235 | 245 | ||
236 | switch (iu->iu_id) { | 246 | switch (iu->iu_id) { |
237 | case IU_ID_STATUS: | 247 | case IU_ID_STATUS: |
@@ -260,6 +270,10 @@ static void uas_stat_cmplt(struct urb *urb) | |||
260 | case IU_ID_WRITE_READY: | 270 | case IU_ID_WRITE_READY: |
261 | uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB); | 271 | uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB); |
262 | break; | 272 | break; |
273 | case IU_ID_RESPONSE: | ||
274 | /* store results for uas_eh_task_mgmt() */ | ||
275 | memcpy(&devinfo->response, iu, sizeof(devinfo->response)); | ||
276 | break; | ||
263 | default: | 277 | default: |
264 | scmd_printk(KERN_ERR, cmnd, | 278 | scmd_printk(KERN_ERR, cmnd, |
265 | "Bogus IU (%d) received on status pipe\n", iu->iu_id); | 279 | "Bogus IU (%d) received on status pipe\n", iu->iu_id); |
@@ -287,6 +301,9 @@ static void uas_data_cmplt(struct urb *urb) | |||
287 | } else { | 301 | } else { |
288 | sdb->resid = sdb->length - urb->actual_length; | 302 | sdb->resid = sdb->length - urb->actual_length; |
289 | } | 303 | } |
304 | if (cmdinfo->aborted) { | ||
305 | return; | ||
306 | } | ||
290 | uas_try_complete(cmnd, __func__); | 307 | uas_try_complete(cmnd, __func__); |
291 | } | 308 | } |
292 | 309 | ||
@@ -377,6 +394,51 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, | |||
377 | return NULL; | 394 | return NULL; |
378 | } | 395 | } |
379 | 396 | ||
397 | static int uas_submit_task_urb(struct scsi_cmnd *cmnd, gfp_t gfp, | ||
398 | u8 function, u16 stream_id) | ||
399 | { | ||
400 | struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; | ||
401 | struct usb_device *udev = devinfo->udev; | ||
402 | struct urb *urb = usb_alloc_urb(0, gfp); | ||
403 | struct task_mgmt_iu *iu; | ||
404 | int err = -ENOMEM; | ||
405 | |||
406 | if (!urb) | ||
407 | goto err; | ||
408 | |||
409 | iu = kzalloc(sizeof(*iu), gfp); | ||
410 | if (!iu) | ||
411 | goto err; | ||
412 | |||
413 | iu->iu_id = IU_ID_TASK_MGMT; | ||
414 | iu->tag = cpu_to_be16(stream_id); | ||
415 | int_to_scsilun(cmnd->device->lun, &iu->lun); | ||
416 | |||
417 | iu->function = function; | ||
418 | switch (function) { | ||
419 | case TMF_ABORT_TASK: | ||
420 | if (blk_rq_tagged(cmnd->request)) | ||
421 | iu->task_tag = cpu_to_be16(cmnd->request->tag + 2); | ||
422 | else | ||
423 | iu->task_tag = cpu_to_be16(1); | ||
424 | break; | ||
425 | } | ||
426 | |||
427 | usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu), | ||
428 | usb_free_urb, NULL); | ||
429 | urb->transfer_flags |= URB_FREE_BUFFER; | ||
430 | |||
431 | err = usb_submit_urb(urb, gfp); | ||
432 | if (err) | ||
433 | goto err; | ||
434 | |||
435 | return 0; | ||
436 | |||
437 | err: | ||
438 | usb_free_urb(urb); | ||
439 | return err; | ||
440 | } | ||
441 | |||
380 | /* | 442 | /* |
381 | * Why should I request the Status IU before sending the Command IU? Spec | 443 | * Why should I request the Status IU before sending the Command IU? Spec |
382 | * says to, but also says the device may receive them in any order. Seems | 444 | * says to, but also says the device may receive them in any order. Seems |
@@ -502,6 +564,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, | |||
502 | 564 | ||
503 | cmdinfo->state = SUBMIT_STATUS_URB | | 565 | cmdinfo->state = SUBMIT_STATUS_URB | |
504 | ALLOC_CMD_URB | SUBMIT_CMD_URB; | 566 | ALLOC_CMD_URB | SUBMIT_CMD_URB; |
567 | cmdinfo->aborted = 0; | ||
505 | 568 | ||
506 | switch (cmnd->sc_data_direction) { | 569 | switch (cmnd->sc_data_direction) { |
507 | case DMA_FROM_DEVICE: | 570 | case DMA_FROM_DEVICE: |
@@ -537,34 +600,66 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, | |||
537 | 600 | ||
538 | static DEF_SCSI_QCMD(uas_queuecommand) | 601 | static DEF_SCSI_QCMD(uas_queuecommand) |
539 | 602 | ||
540 | static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) | 603 | static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, |
604 | const char *fname, u8 function) | ||
541 | { | 605 | { |
542 | uas_log_cmd_state(cmnd, __func__); | 606 | struct Scsi_Host *shost = cmnd->device->host; |
607 | struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; | ||
608 | u16 tag = 9999; /* FIXME */ | ||
543 | 609 | ||
544 | /* XXX: Send ABORT TASK Task Management command */ | 610 | memset(&devinfo->response, 0, sizeof(devinfo->response)); |
545 | return FAILED; | 611 | if (uas_submit_sense_urb(shost, GFP_NOIO, tag)) { |
612 | shost_printk(KERN_INFO, shost, | ||
613 | "%s: %s: submit sense urb failed\n", | ||
614 | __func__, fname); | ||
615 | return FAILED; | ||
616 | } | ||
617 | if (uas_submit_task_urb(cmnd, GFP_NOIO, function, tag)) { | ||
618 | shost_printk(KERN_INFO, shost, | ||
619 | "%s: %s: submit task mgmt urb failed\n", | ||
620 | __func__, fname); | ||
621 | return FAILED; | ||
622 | } | ||
623 | if (0 == usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000)) { | ||
624 | shost_printk(KERN_INFO, shost, | ||
625 | "%s: %s timed out\n", __func__, fname); | ||
626 | return FAILED; | ||
627 | } | ||
628 | if (be16_to_cpu(devinfo->response.tag) != tag) { | ||
629 | shost_printk(KERN_INFO, shost, | ||
630 | "%s: %s failed (wrong tag %d/%d)\n", __func__, | ||
631 | fname, be16_to_cpu(devinfo->response.tag), tag); | ||
632 | return FAILED; | ||
633 | } | ||
634 | if (devinfo->response.response_code != RC_TMF_COMPLETE) { | ||
635 | shost_printk(KERN_INFO, shost, | ||
636 | "%s: %s failed (rc 0x%x)\n", __func__, | ||
637 | fname, devinfo->response.response_code); | ||
638 | return FAILED; | ||
639 | } | ||
640 | return SUCCESS; | ||
546 | } | 641 | } |
547 | 642 | ||
548 | static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd) | 643 | static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) |
549 | { | 644 | { |
550 | struct scsi_device *sdev = cmnd->device; | 645 | struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; |
551 | sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__, | 646 | int ret; |
552 | cmnd->request->tag); | ||
553 | 647 | ||
554 | /* XXX: Send LOGICAL UNIT RESET Task Management command */ | 648 | uas_log_cmd_state(cmnd, __func__); |
555 | return FAILED; | 649 | cmdinfo->aborted = 1; |
650 | ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK); | ||
651 | if (cmdinfo->state & DATA_IN_URB_INFLIGHT) | ||
652 | usb_kill_urb(cmdinfo->data_in_urb); | ||
653 | if (cmdinfo->state & DATA_OUT_URB_INFLIGHT) | ||
654 | usb_kill_urb(cmdinfo->data_out_urb); | ||
655 | return ret; | ||
556 | } | 656 | } |
557 | 657 | ||
558 | static int uas_eh_target_reset_handler(struct scsi_cmnd *cmnd) | 658 | static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd) |
559 | { | 659 | { |
560 | struct scsi_device *sdev = cmnd->device; | 660 | sdev_printk(KERN_INFO, cmnd->device, "%s\n", __func__); |
561 | sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__, | 661 | return uas_eh_task_mgmt(cmnd, "LOGICAL UNIT RESET", |
562 | cmnd->request->tag); | 662 | TMF_LOGICAL_UNIT_RESET); |
563 | |||
564 | /* XXX: Can we reset just the one USB interface? | ||
565 | * Would calling usb_set_interface() have the right effect? | ||
566 | */ | ||
567 | return FAILED; | ||
568 | } | 663 | } |
569 | 664 | ||
570 | static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) | 665 | static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) |
@@ -572,14 +667,21 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd) | |||
572 | struct scsi_device *sdev = cmnd->device; | 667 | struct scsi_device *sdev = cmnd->device; |
573 | struct uas_dev_info *devinfo = sdev->hostdata; | 668 | struct uas_dev_info *devinfo = sdev->hostdata; |
574 | struct usb_device *udev = devinfo->udev; | 669 | struct usb_device *udev = devinfo->udev; |
670 | int err; | ||
575 | 671 | ||
576 | sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__, | 672 | devinfo->resetting = 1; |
577 | cmnd->request->tag); | 673 | usb_kill_anchored_urbs(&devinfo->sense_urbs); |
674 | usb_kill_anchored_urbs(&devinfo->data_urbs); | ||
675 | err = usb_reset_device(udev); | ||
676 | devinfo->resetting = 0; | ||
578 | 677 | ||
579 | if (usb_reset_device(udev)) | 678 | if (err) { |
580 | return SUCCESS; | 679 | shost_printk(KERN_INFO, sdev->host, "%s FAILED\n", __func__); |
680 | return FAILED; | ||
681 | } | ||
581 | 682 | ||
582 | return FAILED; | 683 | shost_printk(KERN_INFO, sdev->host, "%s success\n", __func__); |
684 | return SUCCESS; | ||
583 | } | 685 | } |
584 | 686 | ||
585 | static int uas_slave_alloc(struct scsi_device *sdev) | 687 | static int uas_slave_alloc(struct scsi_device *sdev) |
@@ -604,7 +706,6 @@ static struct scsi_host_template uas_host_template = { | |||
604 | .slave_configure = uas_slave_configure, | 706 | .slave_configure = uas_slave_configure, |
605 | .eh_abort_handler = uas_eh_abort_handler, | 707 | .eh_abort_handler = uas_eh_abort_handler, |
606 | .eh_device_reset_handler = uas_eh_device_reset_handler, | 708 | .eh_device_reset_handler = uas_eh_device_reset_handler, |
607 | .eh_target_reset_handler = uas_eh_target_reset_handler, | ||
608 | .eh_bus_reset_handler = uas_eh_bus_reset_handler, | 709 | .eh_bus_reset_handler = uas_eh_bus_reset_handler, |
609 | .can_queue = 65536, /* Is there a limit on the _host_ ? */ | 710 | .can_queue = 65536, /* Is there a limit on the _host_ ? */ |
610 | .this_id = -1, | 711 | .this_id = -1, |
@@ -766,6 +867,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) | |||
766 | 867 | ||
767 | devinfo->intf = intf; | 868 | devinfo->intf = intf; |
768 | devinfo->udev = udev; | 869 | devinfo->udev = udev; |
870 | devinfo->resetting = 0; | ||
769 | init_usb_anchor(&devinfo->sense_urbs); | 871 | init_usb_anchor(&devinfo->sense_urbs); |
770 | init_usb_anchor(&devinfo->data_urbs); | 872 | init_usb_anchor(&devinfo->data_urbs); |
771 | uas_configure_endpoints(devinfo); | 873 | uas_configure_endpoints(devinfo); |
diff --git a/include/linux/usb/uas.h b/include/linux/usb/uas.h index 9a988e413694..5499ab5c94bd 100644 --- a/include/linux/usb/uas.h +++ b/include/linux/usb/uas.h | |||
@@ -20,6 +20,28 @@ enum { | |||
20 | IU_ID_WRITE_READY = 0x07, | 20 | IU_ID_WRITE_READY = 0x07, |
21 | }; | 21 | }; |
22 | 22 | ||
23 | enum { | ||
24 | TMF_ABORT_TASK = 0x01, | ||
25 | TMF_ABORT_TASK_SET = 0x02, | ||
26 | TMF_CLEAR_TASK_SET = 0x04, | ||
27 | TMF_LOGICAL_UNIT_RESET = 0x08, | ||
28 | TMF_I_T_NEXUS_RESET = 0x10, | ||
29 | TMF_CLEAR_ACA = 0x40, | ||
30 | TMF_QUERY_TASK = 0x80, | ||
31 | TMF_QUERY_TASK_SET = 0x81, | ||
32 | TMF_QUERY_ASYNC_EVENT = 0x82, | ||
33 | }; | ||
34 | |||
35 | enum { | ||
36 | RC_TMF_COMPLETE = 0x00, | ||
37 | RC_INVALID_INFO_UNIT = 0x02, | ||
38 | RC_TMF_NOT_SUPPORTED = 0x04, | ||
39 | RC_TMF_FAILED = 0x05, | ||
40 | RC_TMF_SUCCEEDED = 0x08, | ||
41 | RC_INCORRECT_LUN = 0x09, | ||
42 | RC_OVERLAPPED_TAG = 0x0a, | ||
43 | }; | ||
44 | |||
23 | struct command_iu { | 45 | struct command_iu { |
24 | __u8 iu_id; | 46 | __u8 iu_id; |
25 | __u8 rsvd1; | 47 | __u8 rsvd1; |
@@ -32,6 +54,16 @@ struct command_iu { | |||
32 | __u8 cdb[16]; /* XXX: Overflow-checking tools may misunderstand */ | 54 | __u8 cdb[16]; /* XXX: Overflow-checking tools may misunderstand */ |
33 | }; | 55 | }; |
34 | 56 | ||
57 | struct task_mgmt_iu { | ||
58 | __u8 iu_id; | ||
59 | __u8 rsvd1; | ||
60 | __be16 tag; | ||
61 | __u8 function; | ||
62 | __u8 rsvd2; | ||
63 | __be16 task_tag; | ||
64 | struct scsi_lun lun; | ||
65 | }; | ||
66 | |||
35 | /* | 67 | /* |
36 | * Also used for the Read Ready and Write Ready IUs since they have the | 68 | * Also used for the Read Ready and Write Ready IUs since they have the |
37 | * same first four bytes | 69 | * same first four bytes |
@@ -47,6 +79,14 @@ struct sense_iu { | |||
47 | __u8 sense[SCSI_SENSE_BUFFERSIZE]; | 79 | __u8 sense[SCSI_SENSE_BUFFERSIZE]; |
48 | }; | 80 | }; |
49 | 81 | ||
82 | struct response_ui { | ||
83 | __u8 iu_id; | ||
84 | __u8 rsvd1; | ||
85 | __be16 tag; | ||
86 | __be16 add_response_info; | ||
87 | __u8 response_code; | ||
88 | }; | ||
89 | |||
50 | struct usb_pipe_usage_descriptor { | 90 | struct usb_pipe_usage_descriptor { |
51 | __u8 bLength; | 91 | __u8 bLength; |
52 | __u8 bDescriptorType; | 92 | __u8 bDescriptorType; |