Commit fad44d87 authored by Brian King's avatar Brian King Committed by James Bottomley

[PATCH] sg: Fix oops of sg_cmd_done and sg_release race

The following patch fixes a race condition in sg of sg_cmd_done racing
with sg_release. I've seen this bug hit several times on test machines
and the following patch fixes it. The race is that if srp->done is set
and the waiting thread gets a spurious wakeup immediately afterwards,
then the waiting thread can end up executing and completing, then getting
closed, freeing sfp before the wake_up_interruptible is called, which
then will result in an oops. The oops is fixed by locking around the
setting srp->done to 1 and the wake_up, and also locking around the
checking of srp->done, which guarantees that the wake_up_interruptible
will always occur before the sleeping thread gets a chance to run.
Signed-off-by: default avatarBrian King <brking@us.ibm.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 0212fe0b
...@@ -734,6 +734,18 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, ...@@ -734,6 +734,18 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp,
return 0; return 0;
} }
static int
sg_srp_done(Sg_request *srp, Sg_fd *sfp)
{
unsigned long iflags;
int done;
read_lock_irqsave(&sfp->rq_list_lock, iflags);
done = srp->done;
read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
return done;
}
static int static int
sg_ioctl(struct inode *inode, struct file *filp, sg_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd_in, unsigned long arg) unsigned int cmd_in, unsigned long arg)
...@@ -773,7 +785,7 @@ sg_ioctl(struct inode *inode, struct file *filp, ...@@ -773,7 +785,7 @@ sg_ioctl(struct inode *inode, struct file *filp,
while (1) { while (1) {
result = 0; /* following macro to beat race condition */ result = 0; /* following macro to beat race condition */
__wait_event_interruptible(sfp->read_wait, __wait_event_interruptible(sfp->read_wait,
(sdp->detached || sfp->closed || srp->done), (sdp->detached || sfp->closed || sg_srp_done(srp, sfp)),
result); result);
if (sdp->detached) if (sdp->detached)
return -ENODEV; return -ENODEV;
...@@ -784,7 +796,9 @@ sg_ioctl(struct inode *inode, struct file *filp, ...@@ -784,7 +796,9 @@ sg_ioctl(struct inode *inode, struct file *filp,
srp->orphan = 1; srp->orphan = 1;
return result; /* -ERESTARTSYS because signal hit process */ return result; /* -ERESTARTSYS because signal hit process */
} }
write_lock_irqsave(&sfp->rq_list_lock, iflags);
srp->done = 2; srp->done = 2;
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp); result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp);
return (result < 0) ? result : 0; return (result < 0) ? result : 0;
} }
...@@ -1227,6 +1241,7 @@ sg_cmd_done(Scsi_Cmnd * SCpnt) ...@@ -1227,6 +1241,7 @@ sg_cmd_done(Scsi_Cmnd * SCpnt)
Sg_device *sdp = NULL; Sg_device *sdp = NULL;
Sg_fd *sfp; Sg_fd *sfp;
Sg_request *srp = NULL; Sg_request *srp = NULL;
unsigned long iflags;
if (SCpnt && (SRpnt = SCpnt->sc_request)) if (SCpnt && (SRpnt = SCpnt->sc_request))
srp = (Sg_request *) SRpnt->upper_private_data; srp = (Sg_request *) SRpnt->upper_private_data;
...@@ -1315,8 +1330,10 @@ sg_cmd_done(Scsi_Cmnd * SCpnt) ...@@ -1315,8 +1330,10 @@ sg_cmd_done(Scsi_Cmnd * SCpnt)
if (sfp && srp) { if (sfp && srp) {
/* Now wake up any sg_read() that is waiting for this packet. */ /* Now wake up any sg_read() that is waiting for this packet. */
kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN); kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN);
write_lock_irqsave(&sfp->rq_list_lock, iflags);
srp->done = 1; srp->done = 1;
wake_up_interruptible(&sfp->read_wait); wake_up_interruptible(&sfp->read_wait);
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
} }
} }
...@@ -1510,7 +1527,7 @@ sg_remove(struct class_device *cl_dev) ...@@ -1510,7 +1527,7 @@ sg_remove(struct class_device *cl_dev)
tsfp = sfp->nextfp; tsfp = sfp->nextfp;
for (srp = sfp->headrp; srp; srp = tsrp) { for (srp = sfp->headrp; srp; srp = tsrp) {
tsrp = srp->nextrp; tsrp = srp->nextrp;
if (sfp->closed || (0 == srp->done)) if (sfp->closed || (0 == sg_srp_done(srp, sfp)))
sg_finish_rem_req(srp); sg_finish_rem_req(srp);
} }
if (sfp->closed) { if (sfp->closed) {
...@@ -2492,7 +2509,7 @@ sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp) ...@@ -2492,7 +2509,7 @@ sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp)
for (srp = sfp->headrp; srp; srp = tsrp) { for (srp = sfp->headrp; srp; srp = tsrp) {
tsrp = srp->nextrp; tsrp = srp->nextrp;
if (srp->done) if (sg_srp_done(srp, sfp))
sg_finish_rem_req(srp); sg_finish_rem_req(srp);
else else
++dirty; ++dirty;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment