提交 e0648520 编写于 作者: G Gerd Hoffmann 提交者: Greg Kroah-Hartman

USB: uas: add locking

Add spinlock to protect uas data structures.

[ v2: s/GFP_NOIO/GFP_ATOMIC/, better don't sleep when holding a spinlock ]
Signed-off-by: NGerd Hoffmann <kraxel@redhat.com>
Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
上级 0871d7d8
...@@ -50,6 +50,7 @@ struct uas_dev_info { ...@@ -50,6 +50,7 @@ struct uas_dev_info {
unsigned use_streams:1; unsigned use_streams:1;
unsigned uas_sense_old:1; unsigned uas_sense_old:1;
struct scsi_cmnd *cmnd; struct scsi_cmnd *cmnd;
spinlock_t lock;
}; };
enum { enum {
...@@ -91,6 +92,7 @@ static void uas_do_work(struct work_struct *work) ...@@ -91,6 +92,7 @@ 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 uas_cmd_info *temp;
struct list_head list; struct list_head list;
unsigned long flags;
int err; int err;
spin_lock_irq(&uas_work_lock); spin_lock_irq(&uas_work_lock);
...@@ -101,7 +103,10 @@ static void uas_do_work(struct work_struct *work) ...@@ -101,7 +103,10 @@ static void uas_do_work(struct work_struct *work)
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);
err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO); struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
spin_lock_irqsave(&devinfo->lock, flags);
err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
spin_unlock_irqrestore(&devinfo->lock, flags);
if (err) { if (err) {
list_del(&cmdinfo->list); list_del(&cmdinfo->list);
spin_lock_irq(&uas_work_lock); spin_lock_irq(&uas_work_lock);
...@@ -182,7 +187,9 @@ static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller) ...@@ -182,7 +187,9 @@ static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller)
static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller) static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller)
{ {
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
WARN_ON(!spin_is_locked(&devinfo->lock));
if (cmdinfo->state & (COMMAND_INFLIGHT | if (cmdinfo->state & (COMMAND_INFLIGHT |
DATA_IN_URB_INFLIGHT | DATA_IN_URB_INFLIGHT |
DATA_OUT_URB_INFLIGHT)) DATA_OUT_URB_INFLIGHT))
...@@ -222,6 +229,7 @@ static void uas_stat_cmplt(struct urb *urb) ...@@ -222,6 +229,7 @@ static void uas_stat_cmplt(struct urb *urb)
struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
struct scsi_cmnd *cmnd; struct scsi_cmnd *cmnd;
struct uas_cmd_info *cmdinfo; struct uas_cmd_info *cmdinfo;
unsigned long flags;
u16 tag; u16 tag;
if (urb->status) { if (urb->status) {
...@@ -235,6 +243,7 @@ static void uas_stat_cmplt(struct urb *urb) ...@@ -235,6 +243,7 @@ static void uas_stat_cmplt(struct urb *urb)
return; return;
} }
spin_lock_irqsave(&devinfo->lock, flags);
tag = be16_to_cpup(&iu->tag) - 1; tag = be16_to_cpup(&iu->tag) - 1;
if (tag == 0) if (tag == 0)
cmnd = devinfo->cmnd; cmnd = devinfo->cmnd;
...@@ -243,6 +252,7 @@ static void uas_stat_cmplt(struct urb *urb) ...@@ -243,6 +252,7 @@ static void uas_stat_cmplt(struct urb *urb)
if (!cmnd) { if (!cmnd) {
if (iu->iu_id != IU_ID_RESPONSE) { if (iu->iu_id != IU_ID_RESPONSE) {
usb_free_urb(urb); usb_free_urb(urb);
spin_unlock_irqrestore(&devinfo->lock, flags);
return; return;
} }
} else { } else {
...@@ -262,10 +272,16 @@ static void uas_stat_cmplt(struct urb *urb) ...@@ -262,10 +272,16 @@ static void uas_stat_cmplt(struct urb *urb)
uas_sense(urb, cmnd); uas_sense(urb, cmnd);
if (cmnd->result != 0) { if (cmnd->result != 0) {
/* cancel data transfers on error */ /* cancel data transfers on error */
if (cmdinfo->state & DATA_IN_URB_INFLIGHT) if (cmdinfo->state & DATA_IN_URB_INFLIGHT) {
spin_unlock_irqrestore(&devinfo->lock, flags);
usb_unlink_urb(cmdinfo->data_in_urb); usb_unlink_urb(cmdinfo->data_in_urb);
if (cmdinfo->state & DATA_OUT_URB_INFLIGHT) spin_lock_irqsave(&devinfo->lock, flags);
}
if (cmdinfo->state & DATA_OUT_URB_INFLIGHT) {
spin_unlock_irqrestore(&devinfo->lock, flags);
usb_unlink_urb(cmdinfo->data_out_urb); usb_unlink_urb(cmdinfo->data_out_urb);
spin_lock_irqsave(&devinfo->lock, flags);
}
} }
cmdinfo->state &= ~COMMAND_INFLIGHT; cmdinfo->state &= ~COMMAND_INFLIGHT;
uas_try_complete(cmnd, __func__); uas_try_complete(cmnd, __func__);
...@@ -285,14 +301,18 @@ static void uas_stat_cmplt(struct urb *urb) ...@@ -285,14 +301,18 @@ static void uas_stat_cmplt(struct urb *urb)
"Bogus IU (%d) received on status pipe\n", iu->iu_id); "Bogus IU (%d) received on status pipe\n", iu->iu_id);
} }
usb_free_urb(urb); usb_free_urb(urb);
spin_unlock_irqrestore(&devinfo->lock, flags);
} }
static void uas_data_cmplt(struct urb *urb) static void uas_data_cmplt(struct urb *urb)
{ {
struct scsi_cmnd *cmnd = urb->context; struct scsi_cmnd *cmnd = urb->context;
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
struct scsi_data_buffer *sdb = NULL; struct scsi_data_buffer *sdb = NULL;
unsigned long flags;
spin_lock_irqsave(&devinfo->lock, flags);
if (cmdinfo->data_in_urb == urb) { if (cmdinfo->data_in_urb == urb) {
sdb = scsi_in(cmnd); sdb = scsi_in(cmnd);
cmdinfo->state &= ~DATA_IN_URB_INFLIGHT; cmdinfo->state &= ~DATA_IN_URB_INFLIGHT;
...@@ -308,6 +328,7 @@ static void uas_data_cmplt(struct urb *urb) ...@@ -308,6 +328,7 @@ static void uas_data_cmplt(struct urb *urb)
sdb->resid = sdb->length - urb->actual_length; sdb->resid = sdb->length - urb->actual_length;
} }
uas_try_complete(cmnd, __func__); uas_try_complete(cmnd, __func__);
spin_unlock_irqrestore(&devinfo->lock, flags);
} }
static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
...@@ -474,6 +495,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, ...@@ -474,6 +495,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
int err; int err;
WARN_ON(!spin_is_locked(&devinfo->lock));
if (cmdinfo->state & SUBMIT_STATUS_URB) { if (cmdinfo->state & SUBMIT_STATUS_URB) {
err = uas_submit_sense_urb(cmnd->device->host, gfp, err = uas_submit_sense_urb(cmnd->device->host, gfp,
cmdinfo->stream); cmdinfo->stream);
...@@ -554,12 +576,16 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, ...@@ -554,12 +576,16 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
struct scsi_device *sdev = cmnd->device; struct scsi_device *sdev = cmnd->device;
struct uas_dev_info *devinfo = sdev->hostdata; struct uas_dev_info *devinfo = sdev->hostdata;
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
unsigned long flags;
int err; int err;
BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer)); BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer));
if (devinfo->cmnd) spin_lock_irqsave(&devinfo->lock, flags);
if (devinfo->cmnd) {
spin_unlock_irqrestore(&devinfo->lock, flags);
return SCSI_MLQUEUE_DEVICE_BUSY; return SCSI_MLQUEUE_DEVICE_BUSY;
}
if (blk_rq_tagged(cmnd->request)) { if (blk_rq_tagged(cmnd->request)) {
cmdinfo->stream = cmnd->request->tag + 2; cmdinfo->stream = cmnd->request->tag + 2;
...@@ -594,6 +620,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, ...@@ -594,6 +620,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
if (err) { if (err) {
/* If we did nothing, give up now */ /* If we did nothing, give up now */
if (cmdinfo->state & SUBMIT_STATUS_URB) { if (cmdinfo->state & SUBMIT_STATUS_URB) {
spin_unlock_irqrestore(&devinfo->lock, flags);
return SCSI_MLQUEUE_DEVICE_BUSY; return SCSI_MLQUEUE_DEVICE_BUSY;
} }
spin_lock(&uas_work_lock); spin_lock(&uas_work_lock);
...@@ -602,6 +629,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, ...@@ -602,6 +629,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
schedule_work(&uas_work); schedule_work(&uas_work);
} }
spin_unlock_irqrestore(&devinfo->lock, flags);
return 0; return 0;
} }
...@@ -613,21 +641,25 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, ...@@ -613,21 +641,25 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd,
struct Scsi_Host *shost = cmnd->device->host; struct Scsi_Host *shost = cmnd->device->host;
struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
u16 tag = devinfo->qdepth - 1; u16 tag = devinfo->qdepth - 1;
unsigned long flags;
spin_lock_irqsave(&devinfo->lock, flags);
memset(&devinfo->response, 0, sizeof(devinfo->response)); memset(&devinfo->response, 0, sizeof(devinfo->response));
if (uas_submit_sense_urb(shost, GFP_NOIO, tag)) { if (uas_submit_sense_urb(shost, GFP_ATOMIC, tag)) {
shost_printk(KERN_INFO, shost, shost_printk(KERN_INFO, shost,
"%s: %s: submit sense urb failed\n", "%s: %s: submit sense urb failed\n",
__func__, fname); __func__, fname);
return FAILED; return FAILED;
} }
if (uas_submit_task_urb(cmnd, GFP_NOIO, function, tag)) { if (uas_submit_task_urb(cmnd, GFP_ATOMIC, function, tag)) {
shost_printk(KERN_INFO, shost, shost_printk(KERN_INFO, shost,
"%s: %s: submit task mgmt urb failed\n", "%s: %s: submit task mgmt urb failed\n",
__func__, fname); __func__, fname);
return FAILED; return FAILED;
} }
if (0 == usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000)) { spin_unlock_irqrestore(&devinfo->lock, flags);
if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000) == 0) {
shost_printk(KERN_INFO, shost, shost_printk(KERN_INFO, shost,
"%s: %s timed out\n", __func__, fname); "%s: %s timed out\n", __func__, fname);
return FAILED; return FAILED;
...@@ -650,10 +682,14 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd, ...@@ -650,10 +682,14 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd,
static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
{ {
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
unsigned long flags;
int ret; int ret;
uas_log_cmd_state(cmnd, __func__); uas_log_cmd_state(cmnd, __func__);
spin_lock_irqsave(&devinfo->lock, flags);
cmdinfo->state |= COMMAND_ABORTED; cmdinfo->state |= COMMAND_ABORTED;
spin_unlock_irqrestore(&devinfo->lock, flags);
ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK); ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK);
return ret; return ret;
} }
...@@ -875,6 +911,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) ...@@ -875,6 +911,7 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
init_usb_anchor(&devinfo->cmd_urbs); init_usb_anchor(&devinfo->cmd_urbs);
init_usb_anchor(&devinfo->sense_urbs); init_usb_anchor(&devinfo->sense_urbs);
init_usb_anchor(&devinfo->data_urbs); init_usb_anchor(&devinfo->data_urbs);
spin_lock_init(&devinfo->lock);
uas_configure_endpoints(devinfo); uas_configure_endpoints(devinfo);
result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 3); result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 3);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册