UAS: Re-add workqueue items if submission fails.

If the original submission (or allocation) of the URBs for a SCSI
command fails, the UAS driver sticks the command structure in a
workqueue and schedules uas_do_work() to run.  That function removes the
entire queue before walking across it and attempting to resubmit.

Unfortunately, if the second submission fails, we will leak memory
(because an allocated URB was not submitted) and possibly leave the SCSI
command partially enqueued on some of the stream rings.  Fix this by
checking whether the second submission failed and re-queueing the
command to the UAS workqueue and scheduling it.

Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Cc: Matthew Wilcox <willy@linux.intel.com>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
This commit is contained in:
Sarah Sharp 2011-12-02 11:55:44 -08:00 коммит произвёл Sebastian Andrzej Siewior
Родитель 5611cc4572
Коммит ea9da1c79e
1 изменённых файлов: 13 добавлений и 4 удалений

Просмотреть файл

@ -125,29 +125,38 @@ struct uas_cmd_info {
/* I hate forward declarations, but I actually have a loop */ /* I hate forward declarations, but I actually have a loop */
static int uas_submit_urbs(struct scsi_cmnd *cmnd, static int uas_submit_urbs(struct scsi_cmnd *cmnd,
struct uas_dev_info *devinfo, gfp_t gfp); struct uas_dev_info *devinfo, gfp_t gfp);
static void uas_do_work(struct work_struct *work);
static DECLARE_WORK(uas_work, uas_do_work);
static DEFINE_SPINLOCK(uas_work_lock); static DEFINE_SPINLOCK(uas_work_lock);
static LIST_HEAD(uas_work_list); static LIST_HEAD(uas_work_list);
static void uas_do_work(struct work_struct *work) static void uas_do_work(struct work_struct *work)
{ {
struct uas_cmd_info *cmdinfo; struct uas_cmd_info *cmdinfo;
struct uas_cmd_info *temp;
struct list_head list; struct list_head list;
int err;
spin_lock_irq(&uas_work_lock); spin_lock_irq(&uas_work_lock);
list_replace_init(&uas_work_list, &list); list_replace_init(&uas_work_list, &list);
spin_unlock_irq(&uas_work_lock); spin_unlock_irq(&uas_work_lock);
list_for_each_entry(cmdinfo, &list, list) { list_for_each_entry_safe(cmdinfo, temp, &list, list) {
struct scsi_pointer *scp = (void *)cmdinfo; struct scsi_pointer *scp = (void *)cmdinfo;
struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd *cmnd = container_of(scp,
struct scsi_cmnd, SCp); struct scsi_cmnd, SCp);
uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO); err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO);
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);
}
} }
} }
static DECLARE_WORK(uas_work, uas_do_work);
static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
{ {
struct sense_iu *sense_iu = urb->transfer_buffer; struct sense_iu *sense_iu = urb->transfer_buffer;