Re: [RFC 6/9] UAS: Kill anchored URBs for canceled SCSI commands.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



* Sarah Sharp | 2011-12-02 11:55:54 [-0800]:

>index 2598cbf..f61de1f 100644
>--- a/drivers/usb/storage/uas.c
>+++ b/drivers/usb/storage/uas.c
>@@ -144,19 +144,19 @@ static void uas_do_work(struct work_struct *work)
> 	list_replace_init(&uas_work_list, &list);
> 	spin_unlock_irq(&uas_work_lock);
> 
>+	spin_lock_irq(&uas_work_lock);
> 	list_for_each_entry_safe(cmdinfo, temp, &list, list) {
> 		struct scsi_pointer *scp = (void *)cmdinfo;
> 		struct scsi_cmnd *cmnd = container_of(scp,
> 							struct scsi_cmnd, SCp);
> 		err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO);
>+		list_del_init(&cmdinfo->list);
> 		if (err) {
>-			list_del(&cmdinfo->list);
>-			spin_lock_irq(&uas_work_lock);
> 			list_add_tail(&cmdinfo->list, &uas_work_list);
>-			spin_unlock_irq(&uas_work_lock);
> 			schedule_work(&uas_work);
> 		}
> 	}
>+	spin_unlock_irq(&uas_work_lock);

If you end up in the workqueue due to -ENOMEM and you downgrade
youreself to GFP_ATOMIC (like suggested in this thread) you probably do
better by just dropping the request :)
My recommendation:
- use a mutex while in the list_for_each loop to protect the wq against
  uas_eh_abort_handler().
- add failed again on a new (local) list.
- once the loop is done, grab the spin lock and list_splice() them
  together.

> }
> 
> static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
>@@ -537,11 +538,47 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
> 
> static DEF_SCSI_QCMD(uas_queuecommand)
> 
>+static void uas_kill_tagged_urbs(struct usb_anchor *anchor,
>+		struct uas_cmd_info *cmdinfo)
>+{
>+	usb_kill_anchored_urbs(anchor);
>+
>+	/* Free any URBs that weren't submitted. */
>+	if (cmdinfo->state & SUBMIT_STATUS_URB) {
>+		kfree(cmdinfo->status_urb->transfer_buffer);
>+		usb_free_urb(cmdinfo->status_urb);
>+	}
>+	if (cmdinfo->state & SUBMIT_DATA_IN_URB) {
>+		kfree(cmdinfo->data_in_urb->transfer_buffer);
>+		usb_free_urb(cmdinfo->data_in_urb);
>+	}
>+	if (cmdinfo->state & SUBMIT_DATA_OUT_URB) {
>+		kfree(cmdinfo->data_out_urb->transfer_buffer);
>+		usb_free_urb(cmdinfo->data_out_urb);
>+	}
>+	if (cmdinfo->state & SUBMIT_CMD_URB) {
>+		kfree(cmdinfo->cmd_urb->transfer_buffer);
>+		usb_free_urb(cmdinfo->cmd_urb);

The command and status urb has the URB_FREE_BUFFER flag set. So you
kfree the tranfer_buffer twice for those two.

>+	}
>+}
>+
> static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
> {
> 	struct scsi_device *sdev = cmnd->device;
>+	struct uas_dev_info *devinfo = sdev->hostdata;
>+	struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
>+
> 	sdev_printk(KERN_INFO, sdev, "%s tag %d\n", __func__,
> 							cmnd->request->tag);
>+	spin_lock_irq(&uas_work_lock);
>+	if (!list_empty(&cmdinfo->list))
>+		list_del_init(&cmdinfo->list);
You used INIT_LIST_HEAD() on that one so you can use list_del() on it
without checking. If you plan to do this more than once you have to use
list_del_init().

>+	spin_unlock_irq(&uas_work_lock);
>+
>+	if (blk_rq_tagged(cmnd->request))
>+		uas_kill_tagged_urbs(&devinfo->anchors[cmnd->request->tag], cmdinfo);
>+	else
>+		uas_kill_tagged_urbs(&devinfo->anchors[0], cmdinfo);
> 
> /* XXX: Send ABORT TASK Task Management command */
> 	return FAILED;

Sebastian
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux