diff options
author | James Bottomley <jejb@titanic.(none)> | 2005-08-28 12:18:35 -0400 |
---|---|---|
committer | James Bottomley <jejb@titanic.(none)> | 2005-08-28 12:18:35 -0400 |
commit | 7a93aef7fbac6f4db40b6fec5c0c6b654ae7a93c (patch) | |
tree | 4cd7aae38012dfc1ff6c62be20ef8840e56d8383 /drivers/scsi/st.c | |
parent | 392160335c798bbe94ab3aae6ea0c85d32b81bbc (diff) | |
parent | 8224bfa84d510630b40ea460b2bb380c91acb8ae (diff) |
Merge HEAD from ../scsi-misc-2.6-tmp
Diffstat (limited to 'drivers/scsi/st.c')
-rw-r--r-- | drivers/scsi/st.c | 150 |
1 files changed, 121 insertions, 29 deletions
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 0a7839db5752..9aadf2fcad6a 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c | |||
@@ -17,7 +17,7 @@ | |||
17 | Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support | 17 | Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support |
18 | */ | 18 | */ |
19 | 19 | ||
20 | static char *verstr = "20050501"; | 20 | static char *verstr = "20050802"; |
21 | 21 | ||
22 | #include <linux/module.h> | 22 | #include <linux/module.h> |
23 | 23 | ||
@@ -219,6 +219,12 @@ static int switch_partition(struct scsi_tape *); | |||
219 | 219 | ||
220 | static int st_int_ioctl(struct scsi_tape *, unsigned int, unsigned long); | 220 | static int st_int_ioctl(struct scsi_tape *, unsigned int, unsigned long); |
221 | 221 | ||
222 | static void scsi_tape_release(struct kref *); | ||
223 | |||
224 | #define to_scsi_tape(obj) container_of(obj, struct scsi_tape, kref) | ||
225 | |||
226 | static DECLARE_MUTEX(st_ref_sem); | ||
227 | |||
222 | 228 | ||
223 | #include "osst_detect.h" | 229 | #include "osst_detect.h" |
224 | #ifndef SIGS_FROM_OSST | 230 | #ifndef SIGS_FROM_OSST |
@@ -230,6 +236,46 @@ static int st_int_ioctl(struct scsi_tape *, unsigned int, unsigned long); | |||
230 | {"OnStream", "FW-", "", "osst"} | 236 | {"OnStream", "FW-", "", "osst"} |
231 | #endif | 237 | #endif |
232 | 238 | ||
239 | static struct scsi_tape *scsi_tape_get(int dev) | ||
240 | { | ||
241 | struct scsi_tape *STp = NULL; | ||
242 | |||
243 | down(&st_ref_sem); | ||
244 | write_lock(&st_dev_arr_lock); | ||
245 | |||
246 | if (dev < st_dev_max && scsi_tapes != NULL) | ||
247 | STp = scsi_tapes[dev]; | ||
248 | if (!STp) goto out; | ||
249 | |||
250 | kref_get(&STp->kref); | ||
251 | |||
252 | if (!STp->device) | ||
253 | goto out_put; | ||
254 | |||
255 | if (scsi_device_get(STp->device)) | ||
256 | goto out_put; | ||
257 | |||
258 | goto out; | ||
259 | |||
260 | out_put: | ||
261 | kref_put(&STp->kref, scsi_tape_release); | ||
262 | STp = NULL; | ||
263 | out: | ||
264 | write_unlock(&st_dev_arr_lock); | ||
265 | up(&st_ref_sem); | ||
266 | return STp; | ||
267 | } | ||
268 | |||
269 | static void scsi_tape_put(struct scsi_tape *STp) | ||
270 | { | ||
271 | struct scsi_device *sdev = STp->device; | ||
272 | |||
273 | down(&st_ref_sem); | ||
274 | kref_put(&STp->kref, scsi_tape_release); | ||
275 | scsi_device_put(sdev); | ||
276 | up(&st_ref_sem); | ||
277 | } | ||
278 | |||
233 | struct st_reject_data { | 279 | struct st_reject_data { |
234 | char *vendor; | 280 | char *vendor; |
235 | char *model; | 281 | char *model; |
@@ -311,7 +357,7 @@ static int st_chk_result(struct scsi_tape *STp, struct scsi_request * SRpnt) | |||
311 | return 0; | 357 | return 0; |
312 | 358 | ||
313 | cmdstatp = &STp->buffer->cmdstat; | 359 | cmdstatp = &STp->buffer->cmdstat; |
314 | st_analyze_sense(STp->buffer->last_SRpnt, cmdstatp); | 360 | st_analyze_sense(SRpnt, cmdstatp); |
315 | 361 | ||
316 | if (cmdstatp->have_sense) | 362 | if (cmdstatp->have_sense) |
317 | scode = STp->buffer->cmdstat.sense_hdr.sense_key; | 363 | scode = STp->buffer->cmdstat.sense_hdr.sense_key; |
@@ -399,10 +445,10 @@ static void st_sleep_done(struct scsi_cmnd * SCpnt) | |||
399 | 445 | ||
400 | (STp->buffer)->cmdstat.midlevel_result = SCpnt->result; | 446 | (STp->buffer)->cmdstat.midlevel_result = SCpnt->result; |
401 | SCpnt->request->rq_status = RQ_SCSI_DONE; | 447 | SCpnt->request->rq_status = RQ_SCSI_DONE; |
402 | (STp->buffer)->last_SRpnt = SCpnt->sc_request; | ||
403 | DEB( STp->write_pending = 0; ) | 448 | DEB( STp->write_pending = 0; ) |
404 | 449 | ||
405 | complete(SCpnt->request->waiting); | 450 | if (SCpnt->request->waiting) |
451 | complete(SCpnt->request->waiting); | ||
406 | } | 452 | } |
407 | 453 | ||
408 | /* Do the scsi command. Waits until command performed if do_wait is true. | 454 | /* Do the scsi command. Waits until command performed if do_wait is true. |
@@ -412,8 +458,20 @@ static struct scsi_request * | |||
412 | st_do_scsi(struct scsi_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd, | 458 | st_do_scsi(struct scsi_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd, |
413 | int bytes, int direction, int timeout, int retries, int do_wait) | 459 | int bytes, int direction, int timeout, int retries, int do_wait) |
414 | { | 460 | { |
461 | struct completion *waiting; | ||
415 | unsigned char *bp; | 462 | unsigned char *bp; |
416 | 463 | ||
464 | /* if async, make sure there's no command outstanding */ | ||
465 | if (!do_wait && ((STp->buffer)->last_SRpnt)) { | ||
466 | printk(KERN_ERR "%s: Async command already active.\n", | ||
467 | tape_name(STp)); | ||
468 | if (signal_pending(current)) | ||
469 | (STp->buffer)->syscall_result = (-EINTR); | ||
470 | else | ||
471 | (STp->buffer)->syscall_result = (-EBUSY); | ||
472 | return NULL; | ||
473 | } | ||
474 | |||
417 | if (SRpnt == NULL) { | 475 | if (SRpnt == NULL) { |
418 | SRpnt = scsi_allocate_request(STp->device, GFP_ATOMIC); | 476 | SRpnt = scsi_allocate_request(STp->device, GFP_ATOMIC); |
419 | if (SRpnt == NULL) { | 477 | if (SRpnt == NULL) { |
@@ -427,7 +485,13 @@ st_do_scsi(struct scsi_request * SRpnt, struct scsi_tape * STp, unsigned char *c | |||
427 | } | 485 | } |
428 | } | 486 | } |
429 | 487 | ||
430 | init_completion(&STp->wait); | 488 | /* If async IO, set last_SRpnt. This ptr tells write_behind_check |
489 | which IO is outstanding. It's nulled out when the IO completes. */ | ||
490 | if (!do_wait) | ||
491 | (STp->buffer)->last_SRpnt = SRpnt; | ||
492 | |||
493 | waiting = &STp->wait; | ||
494 | init_completion(waiting); | ||
431 | SRpnt->sr_use_sg = STp->buffer->do_dio || (bytes > (STp->buffer)->frp[0].length); | 495 | SRpnt->sr_use_sg = STp->buffer->do_dio || (bytes > (STp->buffer)->frp[0].length); |
432 | if (SRpnt->sr_use_sg) { | 496 | if (SRpnt->sr_use_sg) { |
433 | if (!STp->buffer->do_dio) | 497 | if (!STp->buffer->do_dio) |
@@ -438,17 +502,20 @@ st_do_scsi(struct scsi_request * SRpnt, struct scsi_tape * STp, unsigned char *c | |||
438 | bp = (STp->buffer)->b_data; | 502 | bp = (STp->buffer)->b_data; |
439 | SRpnt->sr_data_direction = direction; | 503 | SRpnt->sr_data_direction = direction; |
440 | SRpnt->sr_cmd_len = 0; | 504 | SRpnt->sr_cmd_len = 0; |
441 | SRpnt->sr_request->waiting = &(STp->wait); | 505 | SRpnt->sr_request->waiting = waiting; |
442 | SRpnt->sr_request->rq_status = RQ_SCSI_BUSY; | 506 | SRpnt->sr_request->rq_status = RQ_SCSI_BUSY; |
443 | SRpnt->sr_request->rq_disk = STp->disk; | 507 | SRpnt->sr_request->rq_disk = STp->disk; |
508 | SRpnt->sr_request->end_io = blk_end_sync_rq; | ||
444 | STp->buffer->cmdstat.have_sense = 0; | 509 | STp->buffer->cmdstat.have_sense = 0; |
445 | 510 | ||
446 | scsi_do_req(SRpnt, (void *) cmd, bp, bytes, | 511 | scsi_do_req(SRpnt, (void *) cmd, bp, bytes, |
447 | st_sleep_done, timeout, retries); | 512 | st_sleep_done, timeout, retries); |
448 | 513 | ||
449 | if (do_wait) { | 514 | if (do_wait) { |
450 | wait_for_completion(SRpnt->sr_request->waiting); | 515 | wait_for_completion(waiting); |
451 | SRpnt->sr_request->waiting = NULL; | 516 | SRpnt->sr_request->waiting = NULL; |
517 | if (SRpnt->sr_request->rq_status != RQ_SCSI_DONE) | ||
518 | SRpnt->sr_result |= (DRIVER_ERROR << 24); | ||
452 | (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt); | 519 | (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt); |
453 | } | 520 | } |
454 | return SRpnt; | 521 | return SRpnt; |
@@ -465,6 +532,7 @@ static int write_behind_check(struct scsi_tape * STp) | |||
465 | struct st_buffer *STbuffer; | 532 | struct st_buffer *STbuffer; |
466 | struct st_partstat *STps; | 533 | struct st_partstat *STps; |
467 | struct st_cmdstatus *cmdstatp; | 534 | struct st_cmdstatus *cmdstatp; |
535 | struct scsi_request *SRpnt; | ||
468 | 536 | ||
469 | STbuffer = STp->buffer; | 537 | STbuffer = STp->buffer; |
470 | if (!STbuffer->writing) | 538 | if (!STbuffer->writing) |
@@ -478,10 +546,14 @@ static int write_behind_check(struct scsi_tape * STp) | |||
478 | ) /* end DEB */ | 546 | ) /* end DEB */ |
479 | 547 | ||
480 | wait_for_completion(&(STp->wait)); | 548 | wait_for_completion(&(STp->wait)); |
481 | (STp->buffer)->last_SRpnt->sr_request->waiting = NULL; | 549 | SRpnt = STbuffer->last_SRpnt; |
550 | STbuffer->last_SRpnt = NULL; | ||
551 | SRpnt->sr_request->waiting = NULL; | ||
552 | if (SRpnt->sr_request->rq_status != RQ_SCSI_DONE) | ||
553 | SRpnt->sr_result |= (DRIVER_ERROR << 24); | ||
482 | 554 | ||
483 | (STp->buffer)->syscall_result = st_chk_result(STp, (STp->buffer)->last_SRpnt); | 555 | (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt); |
484 | scsi_release_request((STp->buffer)->last_SRpnt); | 556 | scsi_release_request(SRpnt); |
485 | 557 | ||
486 | STbuffer->buffer_bytes -= STbuffer->writing; | 558 | STbuffer->buffer_bytes -= STbuffer->writing; |
487 | STps = &(STp->ps[STp->partition]); | 559 | STps = &(STp->ps[STp->partition]); |
@@ -1055,25 +1127,20 @@ static int st_open(struct inode *inode, struct file *filp) | |||
1055 | */ | 1127 | */ |
1056 | filp->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE); | 1128 | filp->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE); |
1057 | 1129 | ||
1130 | if (!(STp = scsi_tape_get(dev))) | ||
1131 | return -ENXIO; | ||
1132 | |||
1058 | write_lock(&st_dev_arr_lock); | 1133 | write_lock(&st_dev_arr_lock); |
1059 | if (dev >= st_dev_max || scsi_tapes == NULL || | ||
1060 | ((STp = scsi_tapes[dev]) == NULL)) { | ||
1061 | write_unlock(&st_dev_arr_lock); | ||
1062 | return (-ENXIO); | ||
1063 | } | ||
1064 | filp->private_data = STp; | 1134 | filp->private_data = STp; |
1065 | name = tape_name(STp); | 1135 | name = tape_name(STp); |
1066 | 1136 | ||
1067 | if (STp->in_use) { | 1137 | if (STp->in_use) { |
1068 | write_unlock(&st_dev_arr_lock); | 1138 | write_unlock(&st_dev_arr_lock); |
1139 | scsi_tape_put(STp); | ||
1069 | DEB( printk(ST_DEB_MSG "%s: Device already in use.\n", name); ) | 1140 | DEB( printk(ST_DEB_MSG "%s: Device already in use.\n", name); ) |
1070 | return (-EBUSY); | 1141 | return (-EBUSY); |
1071 | } | 1142 | } |
1072 | 1143 | ||
1073 | if(scsi_device_get(STp->device)) { | ||
1074 | write_unlock(&st_dev_arr_lock); | ||
1075 | return (-ENXIO); | ||
1076 | } | ||
1077 | STp->in_use = 1; | 1144 | STp->in_use = 1; |
1078 | write_unlock(&st_dev_arr_lock); | 1145 | write_unlock(&st_dev_arr_lock); |
1079 | STp->rew_at_close = STp->autorew_dev = (iminor(inode) & 0x80) == 0; | 1146 | STp->rew_at_close = STp->autorew_dev = (iminor(inode) & 0x80) == 0; |
@@ -1118,7 +1185,7 @@ static int st_open(struct inode *inode, struct file *filp) | |||
1118 | err_out: | 1185 | err_out: |
1119 | normalize_buffer(STp->buffer); | 1186 | normalize_buffer(STp->buffer); |
1120 | STp->in_use = 0; | 1187 | STp->in_use = 0; |
1121 | scsi_device_put(STp->device); | 1188 | scsi_tape_put(STp); |
1122 | return retval; | 1189 | return retval; |
1123 | 1190 | ||
1124 | } | 1191 | } |
@@ -1250,7 +1317,7 @@ static int st_release(struct inode *inode, struct file *filp) | |||
1250 | write_lock(&st_dev_arr_lock); | 1317 | write_lock(&st_dev_arr_lock); |
1251 | STp->in_use = 0; | 1318 | STp->in_use = 0; |
1252 | write_unlock(&st_dev_arr_lock); | 1319 | write_unlock(&st_dev_arr_lock); |
1253 | scsi_device_put(STp->device); | 1320 | scsi_tape_put(STp); |
1254 | 1321 | ||
1255 | return result; | 1322 | return result; |
1256 | } | 1323 | } |
@@ -3887,6 +3954,7 @@ static int st_probe(struct device *dev) | |||
3887 | goto out_put_disk; | 3954 | goto out_put_disk; |
3888 | } | 3955 | } |
3889 | memset(tpnt, 0, sizeof(struct scsi_tape)); | 3956 | memset(tpnt, 0, sizeof(struct scsi_tape)); |
3957 | kref_init(&tpnt->kref); | ||
3890 | tpnt->disk = disk; | 3958 | tpnt->disk = disk; |
3891 | sprintf(disk->disk_name, "st%d", i); | 3959 | sprintf(disk->disk_name, "st%d", i); |
3892 | disk->private_data = &tpnt->driver; | 3960 | disk->private_data = &tpnt->driver; |
@@ -3902,6 +3970,7 @@ static int st_probe(struct device *dev) | |||
3902 | tpnt->tape_type = MT_ISSCSI2; | 3970 | tpnt->tape_type = MT_ISSCSI2; |
3903 | 3971 | ||
3904 | tpnt->buffer = buffer; | 3972 | tpnt->buffer = buffer; |
3973 | tpnt->buffer->last_SRpnt = NULL; | ||
3905 | 3974 | ||
3906 | tpnt->inited = 0; | 3975 | tpnt->inited = 0; |
3907 | tpnt->dirty = 0; | 3976 | tpnt->dirty = 0; |
@@ -4076,15 +4145,10 @@ static int st_remove(struct device *dev) | |||
4076 | tpnt->modes[mode].cdevs[j] = NULL; | 4145 | tpnt->modes[mode].cdevs[j] = NULL; |
4077 | } | 4146 | } |
4078 | } | 4147 | } |
4079 | tpnt->device = NULL; | ||
4080 | 4148 | ||
4081 | if (tpnt->buffer) { | 4149 | down(&st_ref_sem); |
4082 | tpnt->buffer->orig_frp_segs = 0; | 4150 | kref_put(&tpnt->kref, scsi_tape_release); |
4083 | normalize_buffer(tpnt->buffer); | 4151 | up(&st_ref_sem); |
4084 | kfree(tpnt->buffer); | ||
4085 | } | ||
4086 | put_disk(tpnt->disk); | ||
4087 | kfree(tpnt); | ||
4088 | return 0; | 4152 | return 0; |
4089 | } | 4153 | } |
4090 | } | 4154 | } |
@@ -4093,6 +4157,34 @@ static int st_remove(struct device *dev) | |||
4093 | return 0; | 4157 | return 0; |
4094 | } | 4158 | } |
4095 | 4159 | ||
4160 | /** | ||
4161 | * scsi_tape_release - Called to free the Scsi_Tape structure | ||
4162 | * @kref: pointer to embedded kref | ||
4163 | * | ||
4164 | * st_ref_sem must be held entering this routine. Because it is | ||
4165 | * called on last put, you should always use the scsi_tape_get() | ||
4166 | * scsi_tape_put() helpers which manipulate the semaphore directly | ||
4167 | * and never do a direct kref_put(). | ||
4168 | **/ | ||
4169 | static void scsi_tape_release(struct kref *kref) | ||
4170 | { | ||
4171 | struct scsi_tape *tpnt = to_scsi_tape(kref); | ||
4172 | struct gendisk *disk = tpnt->disk; | ||
4173 | |||
4174 | tpnt->device = NULL; | ||
4175 | |||
4176 | if (tpnt->buffer) { | ||
4177 | tpnt->buffer->orig_frp_segs = 0; | ||
4178 | normalize_buffer(tpnt->buffer); | ||
4179 | kfree(tpnt->buffer); | ||
4180 | } | ||
4181 | |||
4182 | disk->private_data = NULL; | ||
4183 | put_disk(disk); | ||
4184 | kfree(tpnt); | ||
4185 | return; | ||
4186 | } | ||
4187 | |||
4096 | static void st_intr(struct scsi_cmnd *SCpnt) | 4188 | static void st_intr(struct scsi_cmnd *SCpnt) |
4097 | { | 4189 | { |
4098 | scsi_io_completion(SCpnt, (SCpnt->result ? 0: SCpnt->bufflen), 1); | 4190 | scsi_io_completion(SCpnt, (SCpnt->result ? 0: SCpnt->bufflen), 1); |