diff options
author | Jörn Engel <joern@logfs.org> | 2012-04-12 17:33:58 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-05-17 05:08:54 -0400 |
commit | 6acddc5e911bb3a4a007448371ed7317c85669da (patch) | |
tree | ef5ac2ea141645723d2e898f83ed4414dc36f4bc /drivers/scsi/sg.c | |
parent | ebaf466be5afa6c6dca29005374de4534e0e6b33 (diff) |
[SCSI] sg: prevent unwoken sleep
srp->done is protected by sfp->rq_list_lock everywhere, except for this
one case. Result can be that the wake-up happens before the cacheline
with the changed srp->done has arrived, so the waiter can go back to
sleep and never be woken up again.
The wait_event_interruptible() means that anyone trying to debug this
unlikely race will likely notice everything working fine again, as the
next signal will unwedge things. Evil.
Signed-off-by: Joern Engel <joern@logfs.org>
Acked-by: Douglas Gilbert <dgilbert@interlog.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/sg.c')
-rw-r--r-- | drivers/scsi/sg.c | 16 |
1 files changed, 14 insertions, 2 deletions
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 511e3ca1afd6..4a00364445f0 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c | |||
@@ -137,7 +137,8 @@ typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ | |||
137 | char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ | 137 | char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ |
138 | char orphan; /* 1 -> drop on sight, 0 -> normal */ | 138 | char orphan; /* 1 -> drop on sight, 0 -> normal */ |
139 | char sg_io_owned; /* 1 -> packet belongs to SG_IO */ | 139 | char sg_io_owned; /* 1 -> packet belongs to SG_IO */ |
140 | volatile char done; /* 0->before bh, 1->before read, 2->read */ | 140 | /* done protected by rq_list_lock */ |
141 | char done; /* 0->before bh, 1->before read, 2->read */ | ||
141 | struct request *rq; | 142 | struct request *rq; |
142 | struct bio *bio; | 143 | struct bio *bio; |
143 | struct execute_work ew; | 144 | struct execute_work ew; |
@@ -760,6 +761,17 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, | |||
760 | return 0; | 761 | return 0; |
761 | } | 762 | } |
762 | 763 | ||
764 | static int srp_done(Sg_fd *sfp, Sg_request *srp) | ||
765 | { | ||
766 | unsigned long flags; | ||
767 | int ret; | ||
768 | |||
769 | read_lock_irqsave(&sfp->rq_list_lock, flags); | ||
770 | ret = srp->done; | ||
771 | read_unlock_irqrestore(&sfp->rq_list_lock, flags); | ||
772 | return ret; | ||
773 | } | ||
774 | |||
763 | static int | 775 | static int |
764 | sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) | 776 | sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) |
765 | { | 777 | { |
@@ -791,7 +803,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) | |||
791 | if (result < 0) | 803 | if (result < 0) |
792 | return result; | 804 | return result; |
793 | result = wait_event_interruptible(sfp->read_wait, | 805 | result = wait_event_interruptible(sfp->read_wait, |
794 | (srp->done || sdp->detached)); | 806 | (srp_done(sfp, srp) || sdp->detached)); |
795 | if (sdp->detached) | 807 | if (sdp->detached) |
796 | return -ENODEV; | 808 | return -ENODEV; |
797 | write_lock_irq(&sfp->rq_list_lock); | 809 | write_lock_irq(&sfp->rq_list_lock); |