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