diff options
author | James Bottomley <JBottomley@Parallels.com> | 2013-10-25 05:26:38 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2013-10-25 05:59:32 -0400 |
commit | 98481ff0bb8792ebfb832e330e56d3c629ba5fa6 (patch) | |
tree | aa1b08cd6848d0ab8e1110480ef701eb0e2a3ce1 | |
parent | bafc8ad82d482f9ecb9111969a3fdcef366bf8cb (diff) |
[SCSI] Revert "sg: no need sg_open_exclusive_lock"
This reverts commit 00b2d9d6d05b56fc1d77071ff8ccbd2c65b48dec.
This is one of four patches that was causing this bug
[ 205.372823] ================================================
[ 205.372901] [ BUG: lock held when returning to user space! ]
[ 205.372979] 3.12.0-rc6-hw-debug-pagealloc+ #67 Not tainted
[ 205.373055] ------------------------------------------------
[ 205.373132] megarc.bin/5283 is leaving the kernel with locks still held!
[ 205.373212] 1 lock held by megarc.bin/5283:
[ 205.373285] #0: (&sdp->o_sem){.+.+..}, at: [<ffffffff8161e650>] sg_open+0x3a0/0x4d0
Cc: Vaughan Cao <vaughan.cao@oracle.com>
Acked-by: Douglas Gilbert <dgilbert@interlog.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
-rw-r--r-- | drivers/scsi/sg.c | 34 |
1 files changed, 29 insertions, 5 deletions
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index d4af13269e85..4efa9b5884b7 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c | |||
@@ -105,6 +105,8 @@ static int scatter_elem_sz_prev = SG_SCATTER_SZ; | |||
105 | static int sg_add(struct device *, struct class_interface *); | 105 | static int sg_add(struct device *, struct class_interface *); |
106 | static void sg_remove(struct device *, struct class_interface *); | 106 | static void sg_remove(struct device *, struct class_interface *); |
107 | 107 | ||
108 | static DEFINE_SPINLOCK(sg_open_exclusive_lock); | ||
109 | |||
108 | static DEFINE_IDR(sg_index_idr); | 110 | static DEFINE_IDR(sg_index_idr); |
109 | static DEFINE_RWLOCK(sg_index_lock); /* Also used to lock | 111 | static DEFINE_RWLOCK(sg_index_lock); /* Also used to lock |
110 | file descriptor list for device */ | 112 | file descriptor list for device */ |
@@ -174,6 +176,7 @@ typedef struct sg_device { /* holds the state of each scsi generic device */ | |||
174 | struct list_head sfds; | 176 | struct list_head sfds; |
175 | struct rw_semaphore o_sem; /* exclude open should hold this rwsem */ | 177 | struct rw_semaphore o_sem; /* exclude open should hold this rwsem */ |
176 | volatile char detached; /* 0->attached, 1->detached pending removal */ | 178 | volatile char detached; /* 0->attached, 1->detached pending removal */ |
179 | /* exclude protected by sg_open_exclusive_lock */ | ||
177 | char exclude; /* opened for exclusive access */ | 180 | char exclude; /* opened for exclusive access */ |
178 | char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ | 181 | char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ |
179 | struct gendisk *disk; | 182 | struct gendisk *disk; |
@@ -222,6 +225,27 @@ static int sg_allow_access(struct file *filp, unsigned char *cmd) | |||
222 | return blk_verify_command(cmd, filp->f_mode & FMODE_WRITE); | 225 | return blk_verify_command(cmd, filp->f_mode & FMODE_WRITE); |
223 | } | 226 | } |
224 | 227 | ||
228 | static int get_exclude(Sg_device *sdp) | ||
229 | { | ||
230 | unsigned long flags; | ||
231 | int ret; | ||
232 | |||
233 | spin_lock_irqsave(&sg_open_exclusive_lock, flags); | ||
234 | ret = sdp->exclude; | ||
235 | spin_unlock_irqrestore(&sg_open_exclusive_lock, flags); | ||
236 | return ret; | ||
237 | } | ||
238 | |||
239 | static int set_exclude(Sg_device *sdp, char val) | ||
240 | { | ||
241 | unsigned long flags; | ||
242 | |||
243 | spin_lock_irqsave(&sg_open_exclusive_lock, flags); | ||
244 | sdp->exclude = val; | ||
245 | spin_unlock_irqrestore(&sg_open_exclusive_lock, flags); | ||
246 | return val; | ||
247 | } | ||
248 | |||
225 | static int sfds_list_empty(Sg_device *sdp) | 249 | static int sfds_list_empty(Sg_device *sdp) |
226 | { | 250 | { |
227 | unsigned long flags; | 251 | unsigned long flags; |
@@ -293,7 +317,7 @@ sg_open(struct inode *inode, struct file *filp) | |||
293 | } | 317 | } |
294 | /* Since write lock is held, no need to check sfd_list */ | 318 | /* Since write lock is held, no need to check sfd_list */ |
295 | if (flags & O_EXCL) | 319 | if (flags & O_EXCL) |
296 | sdp->exclude = 1; /* used by release lock */ | 320 | set_exclude(sdp, 1); |
297 | 321 | ||
298 | if (sdp->detached) { | 322 | if (sdp->detached) { |
299 | retval = -ENODEV; | 323 | retval = -ENODEV; |
@@ -313,7 +337,7 @@ sg_open(struct inode *inode, struct file *filp) | |||
313 | retval = -ENOMEM; | 337 | retval = -ENOMEM; |
314 | sem_out: | 338 | sem_out: |
315 | if (flags & O_EXCL) { | 339 | if (flags & O_EXCL) { |
316 | sdp->exclude = 0; /* undo if error */ | 340 | set_exclude(sdp, 0); /* undo if error */ |
317 | up_write(&sdp->o_sem); | 341 | up_write(&sdp->o_sem); |
318 | } else | 342 | } else |
319 | up_read(&sdp->o_sem); | 343 | up_read(&sdp->o_sem); |
@@ -340,8 +364,8 @@ sg_release(struct inode *inode, struct file *filp) | |||
340 | return -ENXIO; | 364 | return -ENXIO; |
341 | SCSI_LOG_TIMEOUT(3, printk("sg_release: %s\n", sdp->disk->disk_name)); | 365 | SCSI_LOG_TIMEOUT(3, printk("sg_release: %s\n", sdp->disk->disk_name)); |
342 | 366 | ||
343 | excl = sdp->exclude; | 367 | excl = get_exclude(sdp); |
344 | sdp->exclude = 0; | 368 | set_exclude(sdp, 0); |
345 | if (excl) | 369 | if (excl) |
346 | up_write(&sdp->o_sem); | 370 | up_write(&sdp->o_sem); |
347 | else | 371 | else |
@@ -2598,7 +2622,7 @@ static int sg_proc_seq_show_debug(struct seq_file *s, void *v) | |||
2598 | scsidp->lun, | 2622 | scsidp->lun, |
2599 | scsidp->host->hostt->emulated); | 2623 | scsidp->host->hostt->emulated); |
2600 | seq_printf(s, " sg_tablesize=%d excl=%d\n", | 2624 | seq_printf(s, " sg_tablesize=%d excl=%d\n", |
2601 | sdp->sg_tablesize, sdp->exclude); | 2625 | sdp->sg_tablesize, get_exclude(sdp)); |
2602 | sg_proc_debug_helper(s, sdp); | 2626 | sg_proc_debug_helper(s, sdp); |
2603 | } | 2627 | } |
2604 | read_unlock_irqrestore(&sg_index_lock, iflags); | 2628 | read_unlock_irqrestore(&sg_index_lock, iflags); |