aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/sg.c
diff options
context:
space:
mode:
authorTony Battersby <tonyb@cybernetics.com>2009-01-20 17:00:09 -0500
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2009-03-12 13:58:05 -0400
commita2dd3b4cea335713b58996bb07b3abcde1175f47 (patch)
tree3097816f8abd5fb57dda669d3e776c35f1455f43 /drivers/scsi/sg.c
parentc6517b7942fad663cc1cf3235cbe4207cf769332 (diff)
[SCSI] sg: fix races with ioctl(SG_IO)
sg_io_owned needs to be set before the command is sent to the midlevel; otherwise, a quickly-completing command may cause a different CPU to see "srp->done == 1 && !srp->sg_io_owned", which would lead to incorrect behavior. Check srp->done and set srp->orphan while holding rq_list_lock to prevent races with sg_rq_end_io(). There is no need to check sfp->closed from read/write/ioctl/poll/etc. since the kernel guarantees that this won't happen. The usefulness of sg_srp_done() was questionable before; now it is definitely not needed. Signed-off-by: Tony Battersby <tonyb@cybernetics.com> Acked-by: Douglas Gilbert <dgilbert@interlog.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/sg.c')
-rw-r--r--drivers/scsi/sg.c39
1 files changed, 14 insertions, 25 deletions
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index b447527555a7..18d079e3990e 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -189,7 +189,7 @@ static ssize_t sg_new_read(Sg_fd * sfp, char __user *buf, size_t count,
189 Sg_request * srp); 189 Sg_request * srp);
190static ssize_t sg_new_write(Sg_fd *sfp, struct file *file, 190static ssize_t sg_new_write(Sg_fd *sfp, struct file *file,
191 const char __user *buf, size_t count, int blocking, 191 const char __user *buf, size_t count, int blocking,
192 int read_only, Sg_request **o_srp); 192 int read_only, int sg_io_owned, Sg_request **o_srp);
193static int sg_common_write(Sg_fd * sfp, Sg_request * srp, 193static int sg_common_write(Sg_fd * sfp, Sg_request * srp,
194 unsigned char *cmnd, int timeout, int blocking); 194 unsigned char *cmnd, int timeout, int blocking);
195static int sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer); 195static int sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer);
@@ -561,7 +561,8 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
561 return -EFAULT; 561 return -EFAULT;
562 blocking = !(filp->f_flags & O_NONBLOCK); 562 blocking = !(filp->f_flags & O_NONBLOCK);
563 if (old_hdr.reply_len < 0) 563 if (old_hdr.reply_len < 0)
564 return sg_new_write(sfp, filp, buf, count, blocking, 0, NULL); 564 return sg_new_write(sfp, filp, buf, count,
565 blocking, 0, 0, NULL);
565 if (count < (SZ_SG_HEADER + 6)) 566 if (count < (SZ_SG_HEADER + 6))
566 return -EIO; /* The minimum scsi command length is 6 bytes. */ 567 return -EIO; /* The minimum scsi command length is 6 bytes. */
567 568
@@ -642,7 +643,7 @@ sg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
642 643
643static ssize_t 644static ssize_t
644sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, 645sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf,
645 size_t count, int blocking, int read_only, 646 size_t count, int blocking, int read_only, int sg_io_owned,
646 Sg_request **o_srp) 647 Sg_request **o_srp)
647{ 648{
648 int k; 649 int k;
@@ -662,6 +663,7 @@ sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf,
662 SCSI_LOG_TIMEOUT(1, printk("sg_new_write: queue full\n")); 663 SCSI_LOG_TIMEOUT(1, printk("sg_new_write: queue full\n"));
663 return -EDOM; 664 return -EDOM;
664 } 665 }
666 srp->sg_io_owned = sg_io_owned;
665 hp = &srp->header; 667 hp = &srp->header;
666 if (__copy_from_user(hp, buf, SZ_SG_IO_HDR)) { 668 if (__copy_from_user(hp, buf, SZ_SG_IO_HDR)) {
667 sg_remove_request(sfp, srp); 669 sg_remove_request(sfp, srp);
@@ -766,18 +768,6 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp,
766} 768}
767 769
768static int 770static int
769sg_srp_done(Sg_request *srp, Sg_fd *sfp)
770{
771 unsigned long iflags;
772 int done;
773
774 read_lock_irqsave(&sfp->rq_list_lock, iflags);
775 done = srp->done;
776 read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
777 return done;
778}
779
780static int
781sg_ioctl(struct inode *inode, struct file *filp, 771sg_ioctl(struct inode *inode, struct file *filp,
782 unsigned int cmd_in, unsigned long arg) 772 unsigned int cmd_in, unsigned long arg)
783{ 773{
@@ -809,27 +799,26 @@ sg_ioctl(struct inode *inode, struct file *filp,
809 return -EFAULT; 799 return -EFAULT;
810 result = 800 result =
811 sg_new_write(sfp, filp, p, SZ_SG_IO_HDR, 801 sg_new_write(sfp, filp, p, SZ_SG_IO_HDR,
812 blocking, read_only, &srp); 802 blocking, read_only, 1, &srp);
813 if (result < 0) 803 if (result < 0)
814 return result; 804 return result;
815 srp->sg_io_owned = 1;
816 while (1) { 805 while (1) {
817 result = 0; /* following macro to beat race condition */ 806 result = 0; /* following macro to beat race condition */
818 __wait_event_interruptible(sfp->read_wait, 807 __wait_event_interruptible(sfp->read_wait,
819 (sdp->detached || sfp->closed || sg_srp_done(srp, sfp)), 808 (srp->done || sdp->detached),
820 result); 809 result);
821 if (sdp->detached) 810 if (sdp->detached)
822 return -ENODEV; 811 return -ENODEV;
823 if (sfp->closed) 812 write_lock_irq(&sfp->rq_list_lock);
824 return 0; /* request packet dropped already */ 813 if (srp->done) {
825 if (0 == result) 814 srp->done = 2;
815 write_unlock_irq(&sfp->rq_list_lock);
826 break; 816 break;
817 }
827 srp->orphan = 1; 818 srp->orphan = 1;
819 write_unlock_irq(&sfp->rq_list_lock);
828 return result; /* -ERESTARTSYS because signal hit process */ 820 return result; /* -ERESTARTSYS because signal hit process */
829 } 821 }
830 write_lock_irqsave(&sfp->rq_list_lock, iflags);
831 srp->done = 2;
832 write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
833 result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp); 822 result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp);
834 return (result < 0) ? result : 0; 823 return (result < 0) ? result : 0;
835 } 824 }