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: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
0871d7d86c
commit
e064852072
|
@ -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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
"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)
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
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);
|
||||||
|
|
Loading…
Reference in New Issue