diff options
Diffstat (limited to 'drivers/scsi/st.c')
-rw-r--r-- | drivers/scsi/st.c | 455 |
1 files changed, 331 insertions, 124 deletions
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index ea78d19c6..384fb6462 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -5,18 +5,19 @@ History: Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara. Contribution and ideas from several people including (in alphabetical - order) Klaus Ehrenfried, Wolfgang Denk, J"org Weule, and - Eric Youngdale. + order) Klaus Ehrenfried, Steve Hirsch, Wolfgang Denk, Andreas Koppenh"ofer, + J"org Weule, and Eric Youngdale. - Copyright 1992, 1993, 1994 Kai Makisara - email makisara@vtinsx.ins.vtt.fi or Kai.Makisara@vtt.fi + Copyright 1992, 1993, 1994, 1995 Kai Makisara + email Kai.Makisara@metla.fi - Last modified: Tue Oct 25 19:37:33 1994 by root@kai.home + Last modified: Sat Feb 18 10:51:25 1995 by root@kai.home */ #include <linux/fs.h> #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/mm.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/mtio.h> @@ -43,6 +44,8 @@ #define ST_TWO_FM 0 +#define ST_FAST_MTEOM 0 + #define ST_BUFFER_WRITES 1 #define ST_ASYNC_WRITES 1 @@ -51,7 +54,7 @@ #define ST_BLOCK_SIZE 1024 -#define ST_MAX_BUFFERS 2 +#define ST_MAX_BUFFERS (2 + ST_EXTRA_DEVS) #define ST_BUFFER_BLOCKS 32 @@ -71,10 +74,11 @@ static int debugging = 1; #endif #define MAX_RETRIES 0 +#define MAX_WRITE_RETRIES 0 #define MAX_READY_RETRIES 5 #define NO_TAPE NOT_READY -#define ST_TIMEOUT 27000 +#define ST_TIMEOUT 90000 #define ST_LONG_TIMEOUT 200000 static int st_nbr_buffers; @@ -86,13 +90,14 @@ static int st_max_buffers = ST_MAX_BUFFERS; static Scsi_Tape * scsi_tapes; static void st_init(void); -static void st_attach(Scsi_Device *); +static int st_attach(Scsi_Device *); static int st_detect(Scsi_Device *); +static void st_detach(Scsi_Device *); struct Scsi_Device_Template st_template = {NULL, "tape", "st", TYPE_TAPE, SCSI_TAPE_MAJOR, 0, 0, 0, 0, st_detect, st_init, - NULL, st_attach, NULL}; + NULL, st_attach, st_detach}; static int st_int_ioctl(struct inode * inode,struct file * file, unsigned int cmd_in, unsigned long arg); @@ -106,36 +111,48 @@ st_chk_result(Scsi_Cmnd * SCpnt) { int dev = SCpnt->request.dev; int result = SCpnt->result; - unsigned char * sense = SCpnt->sense_buffer; + unsigned char * sense = SCpnt->sense_buffer, scode; char *stp; - if (!result && SCpnt->sense_buffer[0] == 0) + if (!result /* && SCpnt->sense_buffer[0] == 0 */ ) return 0; #ifdef DEBUG if (debugging) { printk("st%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n", dev, result, - SCpnt->cmnd[0], SCpnt->cmnd[1], SCpnt->cmnd[2], - SCpnt->cmnd[3], SCpnt->cmnd[4], SCpnt->cmnd[5], + SCpnt->data_cmnd[0], SCpnt->data_cmnd[1], SCpnt->data_cmnd[2], + SCpnt->data_cmnd[3], SCpnt->data_cmnd[4], SCpnt->data_cmnd[5], SCpnt->request_bufflen); if (driver_byte(result) & DRIVER_SENSE) print_sense("st", SCpnt); } #endif -/* if ((sense[0] & 0x70) == 0x70 && - ((sense[2] & 0x80) )) - return 0; */ + scode = sense[2] & 0x0f; + if (!(driver_byte(result) & DRIVER_SENSE) || + ((sense[0] & 0x70) == 0x70 && + scode != NO_SENSE && + scode != RECOVERED_ERROR && + scode != UNIT_ATTENTION && + scode != BLANK_CHECK && + scode != VOLUME_OVERFLOW)) { /* Abnormal conditions for tape */ + printk("st%d: Error %x. ", dev, result); + if (driver_byte(result) & DRIVER_SENSE) + print_sense("st", SCpnt); + else + printk("\n"); + } + if ((sense[0] & 0x70) == 0x70 && - sense[2] == RECOVERED_ERROR + scode == RECOVERED_ERROR #ifdef ST_RECOVERED_WRITE_FATAL - && SCpnt->cmnd[0] != WRITE_6 - && SCpnt->cmnd[0] != WRITE_FILEMARKS + && SCpnt->data_cmnd[0] != WRITE_6 + && SCpnt->data_cmnd[0] != WRITE_FILEMARKS #endif ) { scsi_tapes[dev].recover_count++; scsi_tapes[dev].mt_status->mt_erreg += (1 << MT_ST_SOFTERR_SHIFT); - if (SCpnt->cmnd[0] == READ_6) + if (SCpnt->data_cmnd[0] == READ_6) stp = "read"; - else if (SCpnt->cmnd[0] == WRITE_6) + else if (SCpnt->data_cmnd[0] == WRITE_6) stp = "write"; else stp = "ioctl"; @@ -195,17 +212,19 @@ write_behind_check(int dev) { Scsi_Tape * STp; ST_buffer * STbuffer; + unsigned long flags; STp = &(scsi_tapes[dev]); STbuffer = STp->buffer; + save_flags(flags); cli(); if (STbuffer->last_result < 0) { STbuffer->writing = (- STbuffer->writing); sleep_on( &(STp->waiting) ); STbuffer->writing = (- STbuffer->writing); } - sti(); + restore_flags(flags); if (STbuffer->writing < STbuffer->buffer_bytes) memcpy(STbuffer->b_data, @@ -232,6 +251,7 @@ back_over_eof(int dev) Scsi_Cmnd *SCpnt; Scsi_Tape *STp = &(scsi_tapes[dev]); unsigned char cmd[10]; + unsigned int flags; cmd[0] = SPACE; cmd[1] = 0x01; /* Space FileMarks */ @@ -245,11 +265,17 @@ back_over_eof(int dev) (void *) cmd, (void *) (STp->buffer)->b_data, 0, st_sleep_done, ST_TIMEOUT, MAX_RETRIES); + /* need to do the check with interrupts off. -RAB */ + save_flags(flags); + cli(); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + restore_flags(flags); + SCpnt->request.dev = -1; if ((STp->buffer)->last_result != 0) { printk("st%d: Backing over filemark failed.\n", dev); - (STp->mt_status)->mt_fileno += 1; + if ((STp->mt_status)->mt_fileno >= 0) + (STp->mt_status)->mt_fileno += 1; (STp->mt_status)->mt_blkno = 0; } @@ -263,6 +289,7 @@ flush_write_buffer(int dev) { int offset, transfer, blks; int result; + unsigned int flags; unsigned char cmd[10]; Scsi_Cmnd *SCpnt; Scsi_Tape *STp = &(scsi_tapes[dev]); @@ -305,10 +332,14 @@ flush_write_buffer(int dev) SCpnt->request.dev = dev; scsi_do_cmd (SCpnt, (void *) cmd, (STp->buffer)->b_data, transfer, - st_sleep_done, ST_TIMEOUT, MAX_RETRIES); + st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES); + /* this must be done with interrupts off */ + save_flags (flags); + cli(); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); - + restore_flags(flags); + if ((STp->buffer)->last_result_fatal != 0) { printk("st%d: Error on flush.\n", dev); if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && @@ -348,6 +379,9 @@ flush_buffer(struct inode * inode, struct file * filp, int seek_next) STp = &(scsi_tapes[dev]); STbuffer = STp->buffer; + if (STp->ready != ST_READY) + return 0; + if (STp->rw == ST_WRITING) /* Writing */ return flush_write_buffer(dev); @@ -383,6 +417,7 @@ scsi_tape_open(struct inode * inode, struct file * filp) { int dev; unsigned short flags; + unsigned int processor_flags; int i; unsigned char cmd[10]; Scsi_Cmnd * SCpnt; @@ -415,6 +450,7 @@ scsi_tape_open(struct inode * inode, struct file * filp) STp->dirty = 0; STp->rw = ST_IDLE; + STp->ready = ST_READY; if (STp->eof != ST_EOD) /* Save EOD across opens */ STp->eof = ST_NOEOF; STp->eof_hit = 0; @@ -435,7 +471,12 @@ scsi_tape_open(struct inode * inode, struct file * filp) 0, st_sleep_done, ST_LONG_TIMEOUT, MAX_READY_RETRIES); + + /* this must be done with interrupts off */ + save_flags (processor_flags); + cli(); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + restore_flags(processor_flags); if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */ @@ -449,7 +490,13 @@ scsi_tape_open(struct inode * inode, struct file * filp) 0, st_sleep_done, ST_LONG_TIMEOUT, MAX_READY_RETRIES); - if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + + /* this must be done with interrupts off */ + save_flags (processor_flags); + cli(); + if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + restore_flags(processor_flags); + (STp->mt_status)->mt_fileno = STp->drv_block = 0; STp->eof = ST_NOEOF; } @@ -459,14 +506,22 @@ scsi_tape_open(struct inode * inode, struct file * filp) (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) { (STp->mt_status)->mt_fileno = STp->drv_block = 0 ; printk("st%d: No tape.\n", dev); + STp->ready = ST_NO_TAPE; } else { - printk("st%d: Error %x.\n", dev, SCpnt->result); (STp->mt_status)->mt_fileno = STp->drv_block = (-1); + STp->ready = ST_NOT_READY; } - (STp->buffer)->in_use = 0; - STp->in_use = 0; SCpnt->request.dev = -1; /* Mark as not busy */ - return (-EIO); + (STp->buffer)->in_use = 0; + STp->buffer = NULL; + STp->density = 0; /* Clear the erroneous "residue" */ + STp->write_prot = 0; + STp->block_size = 0; + STp->eof = ST_NOEOF; + (STp->mt_status)->mt_fileno = STp->drv_block = 0; + if (scsi_tapes[dev].device->host->hostt->usage_count) + (*scsi_tapes[dev].device->host->hostt->usage_count)++; + return 0; } SCpnt->sense_buffer[0]=0; @@ -477,7 +532,12 @@ scsi_tape_open(struct inode * inode, struct file * filp) (void *) cmd, (void *) (STp->buffer)->b_data, 6, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES); + + /* this must be done with interrupts off */ + save_flags (processor_flags); + cli(); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + restore_flags(processor_flags); if (!SCpnt->result && !SCpnt->sense_buffer[0]) { STp->max_block = ((STp->buffer)->b_data[1] << 16) | @@ -507,7 +567,12 @@ scsi_tape_open(struct inode * inode, struct file * filp) (void *) cmd, (void *) (STp->buffer)->b_data, 12, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES); + + /* this must be done with interrupts off */ + save_flags (processor_flags); + cli(); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + restore_flags(processor_flags); if ((STp->buffer)->last_result_fatal != 0) { #ifdef DEBUG @@ -533,10 +598,10 @@ scsi_tape_open(struct inode * inode, struct file * filp) (STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11]; #ifdef DEBUG if (debugging) - printk("st%d: Density %x, tape length: %x, blocksize: %d, drv buffer: %d\n", + printk("st%d: Density %x, tape length: %x, drv buffer: %d\n", dev, STp->density, (STp->buffer)->b_data[5] * 65536 + (STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7], - STp->block_size, STp->drv_buffer); + STp->drv_buffer); #endif if (STp->block_size > st_buffer_size) { printk("st%d: Blocksize %d too large for buffer.\n", dev, @@ -575,8 +640,17 @@ scsi_tape_open(struct inode * inode, struct file * filp) if (debugging) printk( "st%d: Write protected\n", dev); #endif + if ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR) { + (STp->buffer)->in_use = 0; + STp->buffer = 0; + STp->in_use = 0; + return (-EROFS); + } } + if (scsi_tapes[dev].device->host->hostt->usage_count) + (*scsi_tapes[dev].device->host->hostt->usage_count)++; + return 0; } @@ -591,6 +665,7 @@ scsi_tape_close(struct inode * inode, struct file * filp) static unsigned char cmd[10]; Scsi_Cmnd * SCpnt; Scsi_Tape * STp; + unsigned int flags; dev = MINOR(inode->i_rdev); rewind = (dev & 0x80) == 0; @@ -616,9 +691,15 @@ scsi_tape_close(struct inode * inode, struct file * filp) SCpnt->request.dev = dev; scsi_do_cmd( SCpnt, (void *) cmd, (void *) (STp->buffer)->b_data, - 0, st_sleep_done, ST_TIMEOUT, MAX_RETRIES); + 0, st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES); + + /* this must be done with interrupts off */ + save_flags (flags); + cli(); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + restore_flags(flags); + if ((STp->buffer)->last_result_fatal != 0) { SCpnt->request.dev = -1; /* Mark as not busy */ @@ -626,7 +707,8 @@ scsi_tape_close(struct inode * inode, struct file * filp) } else { SCpnt->request.dev = -1; /* Mark as not busy */ - (STp->mt_status)->mt_fileno++ ; + if ((STp->mt_status)->mt_fileno >= 0) + (STp->mt_status)->mt_fileno++ ; STp->drv_block = 0; if (STp->two_fm) back_over_eof(dev); @@ -651,9 +733,13 @@ scsi_tape_close(struct inode * inode, struct file * filp) if (rewind) st_int_ioctl(inode, filp, MTREW, 1); - (STp->buffer)->in_use = 0; + if (STp->buffer != NULL) + (STp->buffer)->in_use = 0; STp->in_use = 0; + if (scsi_tapes[dev].device->host->hostt->usage_count) + (*scsi_tapes[dev].device->host->hostt->usage_count)--; + return; } @@ -665,13 +751,17 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count) int dev; int total, do_count, blks, retval, transfer; int write_threshold; + int doing_write = 0; static unsigned char cmd[10]; char *b_point; Scsi_Cmnd * SCpnt; Scsi_Tape * STp; + unsigned int flags; dev = MINOR(inode->i_rdev) & 127; STp = &(scsi_tapes[dev]); + if (STp->ready != ST_READY) + return (-EIO); #ifdef DEBUG if (!STp->in_use) { printk("st%d: Incorrect device.\n", dev); @@ -743,6 +833,7 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count) (STp->block_size != 0 && (STp->buffer)->buffer_bytes + count > write_threshold)) { + doing_write = 1; if (STp->block_size == 0) do_count = count; else { @@ -767,9 +858,14 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count) SCpnt->request.dev = dev; scsi_do_cmd (SCpnt, (void *) cmd, (STp->buffer)->b_data, transfer, - st_sleep_done, ST_TIMEOUT, MAX_RETRIES); + st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES); + - if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + /* this must be done with interrupts off */ + save_flags (flags); + cli(); + if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + restore_flags(flags); if ((STp->buffer)->last_result_fatal != 0) { #ifdef DEBUG @@ -850,7 +946,7 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count) count = 0; } - if ((STp->buffer)->last_result_fatal != 0) { + if (doing_write && (STp->buffer)->last_result_fatal != 0) { SCpnt->request.dev = -1; return (STp->buffer)->last_result_fatal; } @@ -864,7 +960,8 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count) else (STp->buffer)->writing = ((STp->buffer)->buffer_bytes / STp->block_size) * STp->block_size; - STp->dirty = 0; + STp->dirty = !((STp->buffer)->writing == + (STp->buffer)->buffer_bytes); if (STp->block_size == 0) blks = (STp->buffer)->writing; @@ -879,11 +976,12 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count) scsi_do_cmd (SCpnt, (void *) cmd, (STp->buffer)->b_data, (STp->buffer)->writing, - st_sleep_done, ST_TIMEOUT, MAX_RETRIES); + st_sleep_done, ST_TIMEOUT, MAX_WRITE_RETRIES); } else SCpnt->request.dev = -1; /* Mark as not busy */ + STp->at_sm &= (total != 0); return( total); } @@ -898,9 +996,12 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count) static unsigned char cmd[10]; Scsi_Cmnd * SCpnt; Scsi_Tape * STp; + unsigned int flags; dev = MINOR(inode->i_rdev) & 127; STp = &(scsi_tapes[dev]); + if (STp->ready != ST_READY) + return (-EIO); #ifdef DEBUG if (!STp->in_use) { printk("st%d: Incorrect device.\n", dev); @@ -930,8 +1031,8 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count) (STp->buffer)->buffer_bytes); #endif if (((STp->buffer)->buffer_bytes == 0) && - STp->eof == ST_EOM_OK) /* EOM or Blank Check */ - return (-EIO); + (STp->eof == ST_EOM_OK || STp->eof == ST_EOD)) + return (-EIO); /* EOM or Blank Check */ STp->rw = ST_READING; @@ -968,13 +1069,18 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count) SCpnt->request.dev = dev; scsi_do_cmd (SCpnt, (void *) cmd, (STp->buffer)->b_data, - (STp->buffer)->buffer_size, - st_sleep_done, ST_TIMEOUT, MAX_RETRIES); + bytes, st_sleep_done, ST_TIMEOUT, MAX_RETRIES); + + /* this must be done with interrupts off */ + save_flags (flags); + cli(); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + restore_flags(flags); (STp->buffer)->read_pointer = 0; STp->eof_hit = 0; + STp->at_sm = 0; if ((STp->buffer)->last_result_fatal) { #ifdef DEBUG @@ -1105,7 +1211,8 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count) STp->drv_block = 0; if (STp->moves_after_eof > 1) STp->moves_after_eof = 0; - (STp->mt_status)->mt_fileno++; + if ((STp->mt_status)->mt_fileno >= 0) + (STp->mt_status)->mt_fileno++; } if (total == 0 && STp->eof == ST_EOM_OK) return (-EIO); /* ST_EOM_ERROR not used in read */ @@ -1138,14 +1245,15 @@ st_set_options(struct inode * inode, long options) STp->do_async_writes = (options & MT_ST_ASYNC_WRITES) != 0; STp->do_read_ahead = (options & MT_ST_READ_AHEAD) != 0; STp->two_fm = (options & MT_ST_TWO_FM) != 0; + STp->fast_mteom = (options & MT_ST_FAST_MTEOM) != 0; #ifdef DEBUG debugging = (options & MT_ST_DEBUGGING) != 0; printk( "st%d: options: buffer writes: %d, async writes: %d, read ahead: %d\n", dev, STp->do_buffer_writes, STp->do_async_writes, STp->do_read_ahead); - printk(" two FMs: %d, debugging: %d\n", STp->two_fm, - debugging); + printk(" two FMs: %d, fast mteom: %d debugging: %d\n", + STp->two_fm, STp->fast_mteom, debugging); #endif } else if ((options & MT_ST_OPTIONS) == MT_ST_WRITE_THRESHOLD) { @@ -1180,12 +1288,16 @@ st_int_ioctl(struct inode * inode,struct file * file, unsigned char cmd[10]; Scsi_Cmnd * SCpnt; Scsi_Tape * STp; - int fileno, blkno, undone, datalen; + int fileno, blkno, at_sm, undone, datalen; + unsigned int flags; dev = dev & 127; STp = &(scsi_tapes[dev]); + if (STp->ready != ST_READY) + return (-EIO); fileno = (STp->mt_status)->mt_fileno ; blkno = STp->drv_block; + at_sm = STp->at_sm; memset(cmd, 0, 10); datalen = 0; @@ -1202,8 +1314,10 @@ st_int_ioctl(struct inode * inode,struct file * file, printk("st%d: Spacing tape forward over %d filemarks.\n", dev, cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); #endif - fileno += arg; + if (fileno >= 0) + fileno += arg; blkno = 0; + at_sm &= (arg != 0); break; case MTBSF: case MTBSFM: @@ -1221,10 +1335,12 @@ st_int_ioctl(struct inode * inode,struct file * file, printk("st%d: Spacing tape backward over %ld filemarks.\n", dev, (-ltmp)); } #endif - fileno -= arg; + if (fileno >= 0) + fileno -= arg; blkno = (-1); /* We can't know the block number */ + at_sm &= (arg != 0); break; - case MTFSR: + case MTFSR: cmd[0] = SPACE; cmd[1] = 0x00; /* Space Blocks */ cmd[2] = (arg >> 16); @@ -1237,6 +1353,7 @@ st_int_ioctl(struct inode * inode,struct file * file, #endif if (blkno >= 0) blkno += arg; + at_sm &= (arg != 0); break; case MTBSR: cmd[0] = SPACE; @@ -1255,8 +1372,9 @@ st_int_ioctl(struct inode * inode,struct file * file, #endif if (blkno >= 0) blkno -= arg; + at_sm &= (arg != 0); break; - case MTFSS: + case MTFSS: cmd[0] = SPACE; cmd[1] = 0x04; /* Space Setmarks */ cmd[2] = (arg >> 16); @@ -1267,8 +1385,10 @@ st_int_ioctl(struct inode * inode,struct file * file, printk("st%d: Spacing tape forward %d setmarks.\n", dev, cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); #endif - if (arg != 0) + if (arg != 0) { blkno = fileno = (-1); + at_sm = 1; + } break; case MTBSS: cmd[0] = SPACE; @@ -1285,8 +1405,10 @@ st_int_ioctl(struct inode * inode,struct file * file, printk("st%d: Spacing tape backward %ld setmarks.\n", dev, (-ltmp)); } #endif - if (arg != 0) + if (arg != 0) { blkno = fileno = (-1); + at_sm = 1; + } break; case MTWEOF: case MTWSM: @@ -1309,8 +1431,10 @@ st_int_ioctl(struct inode * inode,struct file * file, cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); } #endif - fileno += arg; + if (fileno >= 0) + fileno += arg; blkno = 0; + at_sm = (cmd_in == MTWSM); break; case MTREW: cmd[0] = REZERO_UNIT; @@ -1322,7 +1446,7 @@ st_int_ioctl(struct inode * inode,struct file * file, if (debugging) printk("st%d: Rewinding tape.\n", dev); #endif - fileno = blkno = 0 ; + fileno = blkno = at_sm = 0 ; break; case MTOFFL: cmd[0] = START_STOP; @@ -1334,7 +1458,7 @@ st_int_ioctl(struct inode * inode,struct file * file, if (debugging) printk("st%d: Unloading tape.\n", dev); #endif - fileno = blkno = 0 ; + fileno = blkno = at_sm = 0 ; break; case MTNOP: #ifdef DEBUG @@ -1354,34 +1478,47 @@ st_int_ioctl(struct inode * inode,struct file * file, if (debugging) printk("st%d: Retensioning tape.\n", dev); #endif - fileno = blkno = 0 ; + fileno = blkno = at_sm = 0; break; case MTEOM: - /* space to the end of tape */ - ioctl_result = st_int_ioctl(inode, file, MTFSF, 0x3fff); - fileno = (STp->mt_status)->mt_fileno ; - /* The next lines would hide the number of spaced FileMarks - That's why I inserted the previous lines. I had no luck - with detecting EOM with FSF, so we go now to EOM. - Joerg Weule */ + if (!STp->fast_mteom) { + /* space to the end of tape */ + ioctl_result = st_int_ioctl(inode, file, MTFSF, 0x3fff); + fileno = (STp->mt_status)->mt_fileno ; + if (STp->eof == ST_EOD || STp->eof == ST_EOM_OK) + return 0; + /* The next lines would hide the number of spaced FileMarks + That's why I inserted the previous lines. I had no luck + with detecting EOM with FSF, so we go now to EOM. + Joerg Weule */ + } + else + fileno = (-1); cmd[0] = SPACE; cmd[1] = 3; #ifdef DEBUG if (debugging) printk("st%d: Spacing to end of recorded medium.\n", dev); #endif - blkno = (-1); + blkno = 0; + at_sm = 0; break; case MTERASE: if (STp->write_prot) return (-EACCES); cmd[0] = ERASE; cmd[1] = 1; /* To the end of tape */ +#ifdef ST_NOWAIT + cmd[1] |= 2; /* Don't wait for completion */ + timeout = ST_TIMEOUT; +#else + timeout = ST_LONG_TIMEOUT * 8; +#endif #ifdef DEBUG if (debugging) printk("st%d: Erasing tape.\n", dev); #endif - fileno = blkno = 0 ; + fileno = blkno = at_sm = 0 ; break; case MTSEEK: if ((STp->device)->scsi_level < SCSI_2) { @@ -1408,6 +1545,7 @@ st_int_ioctl(struct inode * inode,struct file * file, printk("st%d: Seeking tape to block %ld.\n", dev, arg); #endif fileno = blkno = (-1); + at_sm = 0; break; case MTSETBLK: /* Set block length */ case MTSETDENSITY: /* Set tape density */ @@ -1471,23 +1609,32 @@ st_int_ioctl(struct inode * inode,struct file * file, (void *) cmd, (void *) (STp->buffer)->b_data, datalen, st_sleep_done, timeout, MAX_RETRIES); - if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + + /* this must be done with interrupts off */ + save_flags (flags); + cli(); + if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + restore_flags(flags); + ioctl_result = (STp->buffer)->last_result_fatal; SCpnt->request.dev = -1; /* Mark as not busy */ - if (!ioctl_result) { + if (cmd_in == MTFSF) + STp->moves_after_eof = 0; + else + STp->moves_after_eof = 1; + if (!ioctl_result) { /* SCSI command successful */ if (cmd_in != MTSEEK) { STp->drv_block = blkno; (STp->mt_status)->mt_fileno = fileno; + STp->at_sm = at_sm; } - else + else { STp->drv_block = (STp->mt_status)->mt_fileno = (-1); - if (cmd_in == MTFSF) - STp->moves_after_eof = 0; - else - STp->moves_after_eof = 1; + STp->at_sm = 0; + } if (cmd_in == MTBSFM) ioctl_result = st_int_ioctl(inode, file, MTFSF, 1); else if (cmd_in == MTFSFM) @@ -1512,16 +1659,18 @@ st_int_ioctl(struct inode * inode,struct file * file, else if (cmd_in == MTSETDENSITY) STp->density = arg; else if (cmd_in == MTEOM) { - STp->eof = ST_EOM_OK; + STp->eof = ST_EOD; STp->eof_hit = 0; } else if (cmd_in != MTSETBLK && cmd_in != MTNOP) { STp->eof = ST_NOEOF; STp->eof_hit = 0; } - } else { + } else { /* SCSI command was not completely successful */ if (SCpnt->sense_buffer[2] & 0x40) { - STp->eof = ST_EOM_OK; + if (cmd_in != MTBSF && cmd_in != MTBSFM && + cmd_in != MTBSR && cmd_in != MTBSS) + STp->eof = ST_EOM_OK; STp->eof_hit = 0; STp->drv_block = 0; } @@ -1530,21 +1679,44 @@ st_int_ioctl(struct inode * inode,struct file * file, (SCpnt->sense_buffer[4] << 16) + (SCpnt->sense_buffer[5] << 8) + SCpnt->sense_buffer[6] ); - if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) ) - (STp->mt_status)->mt_fileno = fileno - undone ; - else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) ) + if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) ) { + if (fileno >= 0) + (STp->mt_status)->mt_fileno = fileno - undone ; + else + (STp->mt_status)->mt_fileno = fileno; + STp->drv_block = 0; + } + else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) ) { (STp->mt_status)->mt_fileno = fileno + undone ; + STp->drv_block = 0; + } else if (cmd_in == MTFSR) { - if (blkno >= undone) - STp->drv_block = blkno - undone; - else - STp->drv_block = (-1); + if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */ + (STp->mt_status)->mt_fileno++; + STp->drv_block = 0; + } + else { + if (blkno >= undone) + STp->drv_block = blkno - undone; + else + STp->drv_block = (-1); + } } - else if (cmd_in == MTBSR && blkno >= 0) { - if (blkno >= 0) - STp->drv_block = blkno + undone; - else + else if (cmd_in == MTBSR) { + if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */ + (STp->mt_status)->mt_fileno--; STp->drv_block = (-1); + } + else { + if (blkno >= 0) + STp->drv_block = blkno + undone; + else + STp->drv_block = (-1); + } + } + else if (cmd_in == MTEOM || cmd_in == MTSEEK) { + (STp->mt_status)->mt_fileno = (-1); + STp->drv_block = (-1); } if (STp->eof == ST_NOEOF && (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) @@ -1568,6 +1740,7 @@ st_ioctl(struct inode * inode,struct file * file, unsigned char scmd[10]; Scsi_Cmnd *SCpnt; Scsi_Tape *STp; + unsigned int flags; dev = dev & 127; STp = &(scsi_tapes[dev]); @@ -1590,11 +1763,15 @@ st_ioctl(struct inode * inode,struct file * file, memcpy_fromfs((char *) &mtc, (char *)arg, sizeof(struct mtop)); - i = flush_buffer(inode, file, mtc.mt_op == MTNOP || mtc.mt_op == MTSEEK || + i = flush_buffer(inode, file, mtc.mt_op == MTSEEK || mtc.mt_op == MTREW || mtc.mt_op == MTOFFL || mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM); if (i < 0) return i; + if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK && + mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM && + mtc.mt_op != MTSETDRVBUFFER) + STp->rw = ST_IDLE; /* Prevent automatic WEOF */ if (mtc.mt_op == MTSETDRVBUFFER && (mtc.mt_count & MT_ST_OPTIONS) != 0) @@ -1642,6 +1819,12 @@ st_ioctl(struct inode * inode,struct file * file, (STp->mt_status)->mt_gstat |= GMT_D_1600(0xffffffff); else if (STp->density == 3) (STp->mt_status)->mt_gstat |= GMT_D_6250(0xffffffff); + if (STp->ready == ST_READY) + (STp->mt_status)->mt_gstat |= GMT_ONLINE(0xffffffff); + if (STp->ready == ST_NO_TAPE) + (STp->mt_status)->mt_gstat |= GMT_DR_OPEN(0xffffffff); + if (STp->at_sm) + (STp->mt_status)->mt_gstat |= GMT_SM(0xffffffff); memcpy_tofs((char *)arg, (char *)(STp->mt_status), sizeof(struct mtget)); @@ -1650,6 +1833,8 @@ st_ioctl(struct inode * inode,struct file * file, return 0; } else if (cmd == (MTIOCPOS & IOCCMD_MASK)) { + if (STp->ready != ST_READY) + return (-EIO); #ifdef DEBUG if (debugging) printk("st%d: get tape position.\n", dev); @@ -1683,7 +1868,13 @@ st_ioctl(struct inode * inode,struct file * file, (void *) scmd, (void *) (STp->buffer)->b_data, 20, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES); - if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + + /* this must be done with interrupts off */ + save_flags (flags); + cli(); + if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + restore_flags(flags); + if ((STp->buffer)->last_result_fatal != 0) { mt_pos.mt_blkno = (-1); @@ -1712,8 +1903,10 @@ st_ioctl(struct inode * inode,struct file * file, memcpy_tofs((char *)arg, (char *) (&mt_pos), sizeof(struct mtpos)); return result; } - else + else if (STp->ready == ST_READY) return scsi_ioctl(STp->device, cmd_in, (void *) arg); + else + return (-EIO); } @@ -1748,16 +1941,17 @@ static struct file_operations st_fops = { NULL /* fsync */ }; -static void st_attach(Scsi_Device * SDp){ +static int st_attach(Scsi_Device * SDp){ Scsi_Tape * tpnt; int i; - /* We do not support attaching loadable devices yet. */ - if(scsi_loadable_module_flag) return; - if(SDp->type != TYPE_TAPE) return; + if(SDp->type != TYPE_TAPE) return 1; if(st_template.nr_dev >= st_template.dev_max) - panic ("scsi_devices corrupt (st)"); + { + SDp->attached--; + return 1; + } for(tpnt = scsi_tapes, i=0; i<st_template.dev_max; i++, tpnt++) if(!tpnt->device) break; @@ -1765,13 +1959,17 @@ static void st_attach(Scsi_Device * SDp){ if(i >= st_template.dev_max) panic ("scsi_devices corrupt (st)"); scsi_tapes[i].device = SDp; + if (SDp->scsi_level <= 2) + scsi_tapes[i].mt_status->mt_type = MT_ISSCSI1; + else + scsi_tapes[i].mt_status->mt_type = MT_ISSCSI2; + st_template.nr_dev++; + return 0; }; -static int st_detect(Scsi_Device * SDp){ - - /* We do not support attaching loadable devices yet. */ - if(scsi_loadable_module_flag) return 0; +static int st_detect(Scsi_Device * SDp) +{ if(SDp->type != TYPE_TAPE) return 0; printk("Detected scsi tape st%d at scsi%d, id %d, lun %d\n", @@ -1786,7 +1984,6 @@ static void st_init() { int i; Scsi_Tape * STp; - Scsi_Device * SDp; static int st_registered = 0; if (st_template.dev_noticed == 0) return; @@ -1799,19 +1996,18 @@ static void st_init() st_registered++; } - /* We do not support attaching loadable devices yet. */ - if(scsi_loadable_module_flag) return; - - scsi_tapes = (Scsi_Tape *) scsi_init_malloc(st_template.dev_noticed * - sizeof(Scsi_Tape)); - st_template.dev_max = st_template.dev_noticed; + if (scsi_tapes) return; + scsi_tapes = (Scsi_Tape *) scsi_init_malloc( + (st_template.dev_noticed + ST_EXTRA_DEVS) * + sizeof(Scsi_Tape), GFP_ATOMIC); + st_template.dev_max = st_template.dev_noticed + ST_EXTRA_DEVS; #ifdef DEBUG printk("st: Buffer size %d bytes, write threshold %d bytes.\n", st_buffer_size, st_write_threshold); #endif - for (i=0, SDp = scsi_devices; i < st_template.dev_noticed; ++i) { + for (i=0; i < st_template.dev_max; ++i) { STp = &(scsi_tapes[i]); STp->device = NULL; STp->capacity = 0xfffff; @@ -1826,35 +2022,30 @@ static void st_init() STp->do_async_writes = ST_ASYNC_WRITES; STp->do_read_ahead = ST_READ_AHEAD; STp->two_fm = ST_TWO_FM; + STp->fast_mteom = ST_FAST_MTEOM; STp->write_threshold = st_write_threshold; STp->drv_block = 0; STp->moves_after_eof = 1; - STp->mt_status = (struct mtget *) scsi_init_malloc(sizeof(struct mtget)); + STp->at_sm = 0; + STp->mt_status = (struct mtget *) scsi_init_malloc(sizeof(struct mtget), GFP_ATOMIC); /* Initialize status */ memset((void *) scsi_tapes[i].mt_status, 0, sizeof(struct mtget)); - for (; SDp; SDp = SDp->next) - if (SDp->type == TYPE_TAPE) - break; - if (!SDp) - printk("st%d: ERROR: Not found in scsi chain.\n", i); - else { - if (SDp->scsi_level <= 2) - STp->mt_status->mt_type = MT_ISSCSI1; - else - STp->mt_status->mt_type = MT_ISSCSI2; - } - SDp = SDp->next; } /* Allocate the buffers */ - st_nbr_buffers = st_template.dev_noticed; + st_nbr_buffers = st_template.dev_noticed + ST_EXTRA_DEVS; if (st_nbr_buffers > st_max_buffers) st_nbr_buffers = st_max_buffers; st_buffers = (ST_buffer **) scsi_init_malloc(st_nbr_buffers * - sizeof(ST_buffer *)); + sizeof(ST_buffer *), GFP_ATOMIC); + /* FIXME - if we are hitting this because we are loading a tape module + as a loadable driver, we should not use kmalloc - it will allocate + a 64Kb region in order to buffer about 32Kb. Try using 31 blocks + instead. */ + for (i=0; i < st_nbr_buffers; i++) { st_buffers[i] = (ST_buffer *) scsi_init_malloc(sizeof(ST_buffer) - - 1 + st_buffer_size); + 1 + st_buffer_size, GFP_ATOMIC | GFP_DMA); #ifdef DEBUG /* printk("st: Buffer address: %p\n", st_buffers[i]); */ #endif @@ -1863,3 +2054,19 @@ static void st_init() } return; } + +static void st_detach(Scsi_Device * SDp) +{ + Scsi_Tape * tpnt; + int i; + + for(tpnt = scsi_tapes, i=0; i<st_template.dev_max; i++, tpnt++) + if(tpnt->device == SDp) { + tpnt->device = NULL; + SDp->attached--; + st_template.nr_dev--; + st_template.dev_noticed--; + return; + } + return; +} |