diff options
Diffstat (limited to 'drivers/scsi/sg.c')
-rw-r--r-- | drivers/scsi/sg.c | 176 |
1 files changed, 81 insertions, 95 deletions
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index df5e961484e1..5cbc4bb1b395 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c | |||
@@ -105,11 +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 | |||
110 | static DEFINE_IDR(sg_index_idr); | 108 | static DEFINE_IDR(sg_index_idr); |
111 | static DEFINE_RWLOCK(sg_index_lock); /* Also used to lock | 109 | static DEFINE_RWLOCK(sg_index_lock); |
112 | file descriptor list for device */ | ||
113 | 110 | ||
114 | static struct class_interface sg_interface = { | 111 | static struct class_interface sg_interface = { |
115 | .add_dev = sg_add, | 112 | .add_dev = sg_add, |
@@ -146,8 +143,7 @@ typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ | |||
146 | } Sg_request; | 143 | } Sg_request; |
147 | 144 | ||
148 | typedef struct sg_fd { /* holds the state of a file descriptor */ | 145 | typedef struct sg_fd { /* holds the state of a file descriptor */ |
149 | /* sfd_siblings is protected by sg_index_lock */ | 146 | struct list_head sfd_siblings; /* protected by sfd_lock of device */ |
150 | struct list_head sfd_siblings; | ||
151 | struct sg_device *parentdp; /* owning device */ | 147 | struct sg_device *parentdp; /* owning device */ |
152 | wait_queue_head_t read_wait; /* queue read until command done */ | 148 | wait_queue_head_t read_wait; /* queue read until command done */ |
153 | rwlock_t rq_list_lock; /* protect access to list in req_arr */ | 149 | rwlock_t rq_list_lock; /* protect access to list in req_arr */ |
@@ -170,13 +166,12 @@ typedef struct sg_fd { /* holds the state of a file descriptor */ | |||
170 | 166 | ||
171 | typedef struct sg_device { /* holds the state of each scsi generic device */ | 167 | typedef struct sg_device { /* holds the state of each scsi generic device */ |
172 | struct scsi_device *device; | 168 | struct scsi_device *device; |
173 | wait_queue_head_t o_excl_wait; /* queue open() when O_EXCL in use */ | ||
174 | int sg_tablesize; /* adapter's max scatter-gather table size */ | 169 | int sg_tablesize; /* adapter's max scatter-gather table size */ |
175 | u32 index; /* device index number */ | 170 | u32 index; /* device index number */ |
176 | /* sfds is protected by sg_index_lock */ | 171 | spinlock_t sfd_lock; /* protect file descriptor list for device */ |
177 | struct list_head sfds; | 172 | struct list_head sfds; |
173 | struct rw_semaphore o_sem; /* exclude open should hold this rwsem */ | ||
178 | volatile char detached; /* 0->attached, 1->detached pending removal */ | 174 | volatile char detached; /* 0->attached, 1->detached pending removal */ |
179 | /* exclude protected by sg_open_exclusive_lock */ | ||
180 | char exclude; /* opened for exclusive access */ | 175 | char exclude; /* opened for exclusive access */ |
181 | char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ | 176 | char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ |
182 | struct gendisk *disk; | 177 | struct gendisk *disk; |
@@ -225,35 +220,14 @@ static int sg_allow_access(struct file *filp, unsigned char *cmd) | |||
225 | return blk_verify_command(cmd, filp->f_mode & FMODE_WRITE); | 220 | return blk_verify_command(cmd, filp->f_mode & FMODE_WRITE); |
226 | } | 221 | } |
227 | 222 | ||
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 | |||
249 | static int sfds_list_empty(Sg_device *sdp) | 223 | static int sfds_list_empty(Sg_device *sdp) |
250 | { | 224 | { |
251 | unsigned long flags; | 225 | unsigned long flags; |
252 | int ret; | 226 | int ret; |
253 | 227 | ||
254 | read_lock_irqsave(&sg_index_lock, flags); | 228 | spin_lock_irqsave(&sdp->sfd_lock, flags); |
255 | ret = list_empty(&sdp->sfds); | 229 | ret = list_empty(&sdp->sfds); |
256 | read_unlock_irqrestore(&sg_index_lock, flags); | 230 | spin_unlock_irqrestore(&sdp->sfd_lock, flags); |
257 | return ret; | 231 | return ret; |
258 | } | 232 | } |
259 | 233 | ||
@@ -265,7 +239,6 @@ sg_open(struct inode *inode, struct file *filp) | |||
265 | struct request_queue *q; | 239 | struct request_queue *q; |
266 | Sg_device *sdp; | 240 | Sg_device *sdp; |
267 | Sg_fd *sfp; | 241 | Sg_fd *sfp; |
268 | int res; | ||
269 | int retval; | 242 | int retval; |
270 | 243 | ||
271 | nonseekable_open(inode, filp); | 244 | nonseekable_open(inode, filp); |
@@ -294,54 +267,52 @@ sg_open(struct inode *inode, struct file *filp) | |||
294 | goto error_out; | 267 | goto error_out; |
295 | } | 268 | } |
296 | 269 | ||
297 | if (flags & O_EXCL) { | 270 | if ((flags & O_EXCL) && (O_RDONLY == (flags & O_ACCMODE))) { |
298 | if (O_RDONLY == (flags & O_ACCMODE)) { | 271 | retval = -EPERM; /* Can't lock it with read only access */ |
299 | retval = -EPERM; /* Can't lock it with read only access */ | ||
300 | goto error_out; | ||
301 | } | ||
302 | if (!sfds_list_empty(sdp) && (flags & O_NONBLOCK)) { | ||
303 | retval = -EBUSY; | ||
304 | goto error_out; | ||
305 | } | ||
306 | res = wait_event_interruptible(sdp->o_excl_wait, | ||
307 | ((!sfds_list_empty(sdp) || get_exclude(sdp)) ? 0 : set_exclude(sdp, 1))); | ||
308 | if (res) { | ||
309 | retval = res; /* -ERESTARTSYS because signal hit process */ | ||
310 | goto error_out; | ||
311 | } | ||
312 | } else if (get_exclude(sdp)) { /* some other fd has an exclusive lock on dev */ | ||
313 | if (flags & O_NONBLOCK) { | ||
314 | retval = -EBUSY; | ||
315 | goto error_out; | ||
316 | } | ||
317 | res = wait_event_interruptible(sdp->o_excl_wait, !get_exclude(sdp)); | ||
318 | if (res) { | ||
319 | retval = res; /* -ERESTARTSYS because signal hit process */ | ||
320 | goto error_out; | ||
321 | } | ||
322 | } | ||
323 | if (sdp->detached) { | ||
324 | retval = -ENODEV; | ||
325 | goto error_out; | 272 | goto error_out; |
326 | } | 273 | } |
274 | if (flags & O_NONBLOCK) { | ||
275 | if (flags & O_EXCL) { | ||
276 | if (!down_write_trylock(&sdp->o_sem)) { | ||
277 | retval = -EBUSY; | ||
278 | goto error_out; | ||
279 | } | ||
280 | } else { | ||
281 | if (!down_read_trylock(&sdp->o_sem)) { | ||
282 | retval = -EBUSY; | ||
283 | goto error_out; | ||
284 | } | ||
285 | } | ||
286 | } else { | ||
287 | if (flags & O_EXCL) | ||
288 | down_write(&sdp->o_sem); | ||
289 | else | ||
290 | down_read(&sdp->o_sem); | ||
291 | } | ||
292 | /* Since write lock is held, no need to check sfd_list */ | ||
293 | if (flags & O_EXCL) | ||
294 | sdp->exclude = 1; /* used by release lock */ | ||
295 | |||
327 | if (sfds_list_empty(sdp)) { /* no existing opens on this device */ | 296 | if (sfds_list_empty(sdp)) { /* no existing opens on this device */ |
328 | sdp->sgdebug = 0; | 297 | sdp->sgdebug = 0; |
329 | q = sdp->device->request_queue; | 298 | q = sdp->device->request_queue; |
330 | sdp->sg_tablesize = queue_max_segments(q); | 299 | sdp->sg_tablesize = queue_max_segments(q); |
331 | } | 300 | } |
332 | if ((sfp = sg_add_sfp(sdp, dev))) | 301 | sfp = sg_add_sfp(sdp, dev); |
302 | if (!IS_ERR(sfp)) | ||
333 | filp->private_data = sfp; | 303 | filp->private_data = sfp; |
304 | /* retval is already provably zero at this point because of the | ||
305 | * check after retval = scsi_autopm_get_device(sdp->device)) | ||
306 | */ | ||
334 | else { | 307 | else { |
308 | retval = PTR_ERR(sfp); | ||
309 | |||
335 | if (flags & O_EXCL) { | 310 | if (flags & O_EXCL) { |
336 | set_exclude(sdp, 0); /* undo if error */ | 311 | sdp->exclude = 0; /* undo if error */ |
337 | wake_up_interruptible(&sdp->o_excl_wait); | 312 | up_write(&sdp->o_sem); |
338 | } | 313 | } else |
339 | retval = -ENOMEM; | 314 | up_read(&sdp->o_sem); |
340 | goto error_out; | ||
341 | } | ||
342 | retval = 0; | ||
343 | error_out: | 315 | error_out: |
344 | if (retval) { | ||
345 | scsi_autopm_put_device(sdp->device); | 316 | scsi_autopm_put_device(sdp->device); |
346 | sdp_put: | 317 | sdp_put: |
347 | scsi_device_put(sdp->device); | 318 | scsi_device_put(sdp->device); |
@@ -358,13 +329,18 @@ sg_release(struct inode *inode, struct file *filp) | |||
358 | { | 329 | { |
359 | Sg_device *sdp; | 330 | Sg_device *sdp; |
360 | Sg_fd *sfp; | 331 | Sg_fd *sfp; |
332 | int excl; | ||
361 | 333 | ||
362 | if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) | 334 | if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) |
363 | return -ENXIO; | 335 | return -ENXIO; |
364 | SCSI_LOG_TIMEOUT(3, printk("sg_release: %s\n", sdp->disk->disk_name)); | 336 | SCSI_LOG_TIMEOUT(3, printk("sg_release: %s\n", sdp->disk->disk_name)); |
365 | 337 | ||
366 | set_exclude(sdp, 0); | 338 | excl = sdp->exclude; |
367 | wake_up_interruptible(&sdp->o_excl_wait); | 339 | sdp->exclude = 0; |
340 | if (excl) | ||
341 | up_write(&sdp->o_sem); | ||
342 | else | ||
343 | up_read(&sdp->o_sem); | ||
368 | 344 | ||
369 | scsi_autopm_put_device(sdp->device); | 345 | scsi_autopm_put_device(sdp->device); |
370 | kref_put(&sfp->f_ref, sg_remove_sfp); | 346 | kref_put(&sfp->f_ref, sg_remove_sfp); |
@@ -1415,8 +1391,9 @@ static Sg_device *sg_alloc(struct gendisk *disk, struct scsi_device *scsidp) | |||
1415 | disk->first_minor = k; | 1391 | disk->first_minor = k; |
1416 | sdp->disk = disk; | 1392 | sdp->disk = disk; |
1417 | sdp->device = scsidp; | 1393 | sdp->device = scsidp; |
1394 | spin_lock_init(&sdp->sfd_lock); | ||
1418 | INIT_LIST_HEAD(&sdp->sfds); | 1395 | INIT_LIST_HEAD(&sdp->sfds); |
1419 | init_waitqueue_head(&sdp->o_excl_wait); | 1396 | init_rwsem(&sdp->o_sem); |
1420 | sdp->sg_tablesize = queue_max_segments(q); | 1397 | sdp->sg_tablesize = queue_max_segments(q); |
1421 | sdp->index = k; | 1398 | sdp->index = k; |
1422 | kref_init(&sdp->d_ref); | 1399 | kref_init(&sdp->d_ref); |
@@ -1549,11 +1526,13 @@ static void sg_remove(struct device *cl_dev, struct class_interface *cl_intf) | |||
1549 | 1526 | ||
1550 | /* Need a write lock to set sdp->detached. */ | 1527 | /* Need a write lock to set sdp->detached. */ |
1551 | write_lock_irqsave(&sg_index_lock, iflags); | 1528 | write_lock_irqsave(&sg_index_lock, iflags); |
1529 | spin_lock(&sdp->sfd_lock); | ||
1552 | sdp->detached = 1; | 1530 | sdp->detached = 1; |
1553 | list_for_each_entry(sfp, &sdp->sfds, sfd_siblings) { | 1531 | list_for_each_entry(sfp, &sdp->sfds, sfd_siblings) { |
1554 | wake_up_interruptible(&sfp->read_wait); | 1532 | wake_up_interruptible(&sfp->read_wait); |
1555 | kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP); | 1533 | kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP); |
1556 | } | 1534 | } |
1535 | spin_unlock(&sdp->sfd_lock); | ||
1557 | write_unlock_irqrestore(&sg_index_lock, iflags); | 1536 | write_unlock_irqrestore(&sg_index_lock, iflags); |
1558 | 1537 | ||
1559 | sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic"); | 1538 | sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic"); |
@@ -2064,7 +2043,7 @@ sg_add_sfp(Sg_device * sdp, int dev) | |||
2064 | 2043 | ||
2065 | sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN); | 2044 | sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN); |
2066 | if (!sfp) | 2045 | if (!sfp) |
2067 | return NULL; | 2046 | return ERR_PTR(-ENOMEM); |
2068 | 2047 | ||
2069 | init_waitqueue_head(&sfp->read_wait); | 2048 | init_waitqueue_head(&sfp->read_wait); |
2070 | rwlock_init(&sfp->rq_list_lock); | 2049 | rwlock_init(&sfp->rq_list_lock); |
@@ -2078,9 +2057,13 @@ sg_add_sfp(Sg_device * sdp, int dev) | |||
2078 | sfp->cmd_q = SG_DEF_COMMAND_Q; | 2057 | sfp->cmd_q = SG_DEF_COMMAND_Q; |
2079 | sfp->keep_orphan = SG_DEF_KEEP_ORPHAN; | 2058 | sfp->keep_orphan = SG_DEF_KEEP_ORPHAN; |
2080 | sfp->parentdp = sdp; | 2059 | sfp->parentdp = sdp; |
2081 | write_lock_irqsave(&sg_index_lock, iflags); | 2060 | spin_lock_irqsave(&sdp->sfd_lock, iflags); |
2061 | if (sdp->detached) { | ||
2062 | spin_unlock_irqrestore(&sdp->sfd_lock, iflags); | ||
2063 | return ERR_PTR(-ENODEV); | ||
2064 | } | ||
2082 | list_add_tail(&sfp->sfd_siblings, &sdp->sfds); | 2065 | list_add_tail(&sfp->sfd_siblings, &sdp->sfds); |
2083 | write_unlock_irqrestore(&sg_index_lock, iflags); | 2066 | spin_unlock_irqrestore(&sdp->sfd_lock, iflags); |
2084 | SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p\n", sfp)); | 2067 | SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p\n", sfp)); |
2085 | if (unlikely(sg_big_buff != def_reserved_size)) | 2068 | if (unlikely(sg_big_buff != def_reserved_size)) |
2086 | sg_big_buff = def_reserved_size; | 2069 | sg_big_buff = def_reserved_size; |
@@ -2130,10 +2113,9 @@ static void sg_remove_sfp(struct kref *kref) | |||
2130 | struct sg_device *sdp = sfp->parentdp; | 2113 | struct sg_device *sdp = sfp->parentdp; |
2131 | unsigned long iflags; | 2114 | unsigned long iflags; |
2132 | 2115 | ||
2133 | write_lock_irqsave(&sg_index_lock, iflags); | 2116 | spin_lock_irqsave(&sdp->sfd_lock, iflags); |
2134 | list_del(&sfp->sfd_siblings); | 2117 | list_del(&sfp->sfd_siblings); |
2135 | write_unlock_irqrestore(&sg_index_lock, iflags); | 2118 | spin_unlock_irqrestore(&sdp->sfd_lock, iflags); |
2136 | wake_up_interruptible(&sdp->o_excl_wait); | ||
2137 | 2119 | ||
2138 | INIT_WORK(&sfp->ew.work, sg_remove_sfp_usercontext); | 2120 | INIT_WORK(&sfp->ew.work, sg_remove_sfp_usercontext); |
2139 | schedule_work(&sfp->ew.work); | 2121 | schedule_work(&sfp->ew.work); |
@@ -2520,7 +2502,7 @@ static int sg_proc_seq_show_devstrs(struct seq_file *s, void *v) | |||
2520 | return 0; | 2502 | return 0; |
2521 | } | 2503 | } |
2522 | 2504 | ||
2523 | /* must be called while holding sg_index_lock */ | 2505 | /* must be called while holding sg_index_lock and sfd_lock */ |
2524 | static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) | 2506 | static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) |
2525 | { | 2507 | { |
2526 | int k, m, new_interface, blen, usg; | 2508 | int k, m, new_interface, blen, usg; |
@@ -2605,22 +2587,26 @@ static int sg_proc_seq_show_debug(struct seq_file *s, void *v) | |||
2605 | 2587 | ||
2606 | read_lock_irqsave(&sg_index_lock, iflags); | 2588 | read_lock_irqsave(&sg_index_lock, iflags); |
2607 | sdp = it ? sg_lookup_dev(it->index) : NULL; | 2589 | sdp = it ? sg_lookup_dev(it->index) : NULL; |
2608 | if (sdp && !list_empty(&sdp->sfds)) { | 2590 | if (sdp) { |
2609 | struct scsi_device *scsidp = sdp->device; | 2591 | spin_lock(&sdp->sfd_lock); |
2592 | if (!list_empty(&sdp->sfds)) { | ||
2593 | struct scsi_device *scsidp = sdp->device; | ||
2610 | 2594 | ||
2611 | seq_printf(s, " >>> device=%s ", sdp->disk->disk_name); | 2595 | seq_printf(s, " >>> device=%s ", sdp->disk->disk_name); |
2612 | if (sdp->detached) | 2596 | if (sdp->detached) |
2613 | seq_printf(s, "detached pending close "); | 2597 | seq_printf(s, "detached pending close "); |
2614 | else | 2598 | else |
2615 | seq_printf | 2599 | seq_printf |
2616 | (s, "scsi%d chan=%d id=%d lun=%d em=%d", | 2600 | (s, "scsi%d chan=%d id=%d lun=%d em=%d", |
2617 | scsidp->host->host_no, | 2601 | scsidp->host->host_no, |
2618 | scsidp->channel, scsidp->id, | 2602 | scsidp->channel, scsidp->id, |
2619 | scsidp->lun, | 2603 | scsidp->lun, |
2620 | scsidp->host->hostt->emulated); | 2604 | scsidp->host->hostt->emulated); |
2621 | seq_printf(s, " sg_tablesize=%d excl=%d\n", | 2605 | seq_printf(s, " sg_tablesize=%d excl=%d\n", |
2622 | sdp->sg_tablesize, get_exclude(sdp)); | 2606 | sdp->sg_tablesize, sdp->exclude); |
2623 | sg_proc_debug_helper(s, sdp); | 2607 | sg_proc_debug_helper(s, sdp); |
2608 | } | ||
2609 | spin_unlock(&sdp->sfd_lock); | ||
2624 | } | 2610 | } |
2625 | read_unlock_irqrestore(&sg_index_lock, iflags); | 2611 | read_unlock_irqrestore(&sg_index_lock, iflags); |
2626 | return 0; | 2612 | return 0; |