From 6b38193b2679628cc01a22baa88c2a4c415f4a74 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Kai=20M=C3=A4kisara?= Date: Sun, 13 Apr 2003 20:33:35 -0700 Subject: [PATCH] [PATCH] SCSI tape EOT write fixes This contains the following changes: - EOT detection fixed when writing in fixed block mode - asynchronous writes in fixed block mode and write threshold removed to enable the EOT fixes (the parameter accepted for compatibility) --- Documentation/scsi/st.txt | 11 +-- drivers/scsi/st.c | 171 +++++++++++++++++++++++--------------- drivers/scsi/st.h | 1 - drivers/scsi/st_options.h | 9 +- 4 files changed, 111 insertions(+), 81 deletions(-) diff --git a/Documentation/scsi/st.txt b/Documentation/scsi/st.txt index 30b9c3955c1f3..39f0380b45827 100644 --- a/Documentation/scsi/st.txt +++ b/Documentation/scsi/st.txt @@ -1,8 +1,8 @@ This file contains brief information about the SCSI tape driver. The driver is currently maintained by Kai Mäkisara (email -Kai.Makisara@metla.fi) +Kai.Makisara@kolumbus.fi) -Last modified: Sat Dec 14 14:35:30 2002 by makisara +Last modified: Sun Apr 6 22:44:13 2003 by makisara BASICS @@ -132,7 +132,7 @@ minimum allowable buffer size. Asynchronous writing. Writing the buffer contents to the tape is started and the write call returns immediately. The status is checked at the next tape operation. Asynchronous writes are not done with -direct i/o. +direct i/o and not in fixed block mode. Buffered writes and asynchronous writes may in some rare cases cause problems in multivolume operations if there is not enough space on the @@ -143,11 +143,6 @@ attempted even if the user does not want to get all of the data at this read command. Should be disabled for those drives that don't like a filemark to truncate a read request or that don't like backspacing. -The threshold for triggering asynchronous write in fixed block mode -is defined by ST_WRITE_THRESHOLD. This may be optimized for each -use pattern. The default triggers asynchronous write after three -default sized writes (10 kB) from tar. - Scatter/gather buffers (buffers that consist of chunks non-contiguous in the physical memory) are used if contiguous buffers can't be allocated. To support all SCSI adapters (including those not diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index f8917661cd13c..00f5fb6bea0a9 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -9,16 +9,16 @@ Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky, Michael Schaefer, J"org Weule, and Eric Youngdale. - Copyright 1992 - 2002 Kai Makisara - email Kai.Makisara@metla.fi + Copyright 1992 - 2003 Kai Makisara + email Kai.Makisara@kolumbus.fi - Last modified: Sat Dec 14 14:25:09 2002 by makisara + Last modified: Sat Apr 12 20:20:39 2003 by makisara Some small formal changes - aeb, 950809 Last modified: 18-JAN-1998 Richard Gooch Devfs support */ -static char *verstr = "20021214"; +static char *verstr = "20030412"; #include @@ -65,7 +65,6 @@ static char *verstr = "20021214"; #include "st.h" static int buffer_kbs; -static int write_threshold_kbs; static int max_sg_segs; static int try_direct_io = TRY_DIRECT_IO; static int try_rdio = TRUE; @@ -80,8 +79,6 @@ MODULE_LICENSE("GPL"); MODULE_PARM(buffer_kbs, "i"); MODULE_PARM_DESC(buffer_kbs, "Default driver buffer size for fixed block mode (KB; 32)"); -MODULE_PARM(write_threshold_kbs, "i"); -MODULE_PARM_DESC(write_threshold_kbs, "Asynchronous write threshold (KB; 30)"); MODULE_PARM(max_sg_segs, "i"); MODULE_PARM_DESC(max_sg_segs, "Maximum number of scatter/gather segments to use (256)"); MODULE_PARM(try_direct_io, "i"); @@ -94,6 +91,7 @@ MODULE_PARM(try_wdio, "i"); MODULE_PARM_DESC(try_wdio, "Try direct write i/o when possible"); #ifndef MODULE +static int write_threshold_kbs; /* retained for compatibility */ static struct st_dev_parm { char *name; int *val; @@ -101,11 +99,11 @@ static struct st_dev_parm { { "buffer_kbs", &buffer_kbs }, - { + { /* Retained for compatibility with 2.4 */ "write_threshold_kbs", &write_threshold_kbs }, { - "max_sg_segs", &max_sg_segs + "max_sg_segs", NULL }, { "try_direct_io", &try_direct_io @@ -117,7 +115,6 @@ static struct st_dev_parm { /* The default definitions have been moved to st_options.h */ #define ST_FIXED_BUFFER_SIZE (ST_FIXED_BUFFER_BLOCKS * ST_KILOBYTE) -#define ST_WRITE_THRESHOLD (ST_WRITE_THRESHOLD_BLOCKS * ST_KILOBYTE) /* The buffer size should fit into the 24 bits for length in the 6-byte SCSI read and write commands. */ @@ -146,7 +143,6 @@ DEB( static int debugging = DEBUG; ) static rwlock_t st_dev_arr_lock = RW_LOCK_UNLOCKED; static int st_fixed_buffer_size = ST_FIXED_BUFFER_SIZE; -static int st_write_threshold = ST_WRITE_THRESHOLD; static int st_max_sg_segs = ST_MAX_SG; static Scsi_Tape **scsi_tapes = NULL; @@ -158,6 +154,7 @@ static int enlarge_buffer(ST_buffer *, int, int); static void normalize_buffer(ST_buffer *); static int append_to_buffer(const char *, ST_buffer *, int); static int from_buffer(ST_buffer *, char *, int); +static void move_buffer_data(ST_buffer *, int); static void buf_to_sg(ST_buffer *, unsigned int); static int st_map_user_pages(struct scatterlist *, const unsigned int, @@ -1313,7 +1310,7 @@ static ssize_t ssize_t total; ssize_t i, do_count, blks, transfer; ssize_t retval; - int undone; + int undone, retry_eot = 0, scode; int async_write; unsigned char cmd[MAX_COMMAND_SIZE]; const char *b_point; @@ -1416,7 +1413,7 @@ static ssize_t STps->rw = ST_WRITING; b_point = buf; - while (count > 0) { + while (count > 0 && !retry_eot) { if (STbp->do_dio) { do_count = count; @@ -1441,12 +1438,8 @@ static ssize_t filp->f_pos += do_count; b_point += do_count; - async_write = !STbp->do_dio && + async_write = STp->block_size == 0 && !STbp->do_dio && STm->do_async_writes && STps->eof < ST_EOM_OK; - if (STp->block_size != 0) - async_write &= count == 0 && - (!STm->do_buffer_writes || - STbp->buffer_bytes >= STp->write_threshold); if (STp->block_size != 0 && STm->do_buffer_writes && !(STp->try_dio && try_wdio) && STps->eof < ST_EOM_OK && @@ -1457,12 +1450,15 @@ static ssize_t break; } + retry_write: if (STp->block_size == 0) blks = transfer = do_count; else { if (!STbp->do_dio) - do_count = STbp->buffer_bytes; - blks = do_count / STp->block_size; + blks = STbp->buffer_bytes; + else + blks = do_count; + blks /= STp->block_size; transfer = blks * STp->block_size; } cmd[2] = blks >> 16; @@ -1488,15 +1484,15 @@ static ssize_t DEBC(printk(ST_DEB_MSG "%s: Error on write:\n", name)); if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 && (SRpnt->sr_sense_buffer[2] & 0x40)) { + scode = SRpnt->sr_sense_buffer[2] & 0x0f; if ((SRpnt->sr_sense_buffer[0] & 0x80) != 0) undone = (SRpnt->sr_sense_buffer[3] << 24) | (SRpnt->sr_sense_buffer[4] << 16) | (SRpnt->sr_sense_buffer[5] << 8) | SRpnt->sr_sense_buffer[6]; else if (STp->block_size == 0 && - (SRpnt->sr_sense_buffer[2] & 0x0f) == - VOLUME_OVERFLOW) - undone = do_count; + scode == VOLUME_OVERFLOW) + undone = transfer; else undone = 0; if (STp->block_size != 0) @@ -1504,7 +1500,7 @@ static ssize_t filp->f_pos -= undone; if (undone <= do_count) { /* Only data from this write is not written */ - count -= undone; + count += undone; do_count -= undone; if (STp->block_size) blks = (transfer - undone) / STp->block_size; @@ -1514,21 +1510,40 @@ static ssize_t (retval left to zero) */ if (STp->block_size == 0 || - undone > 0 || count == 0) + undone > 0 || count == 0) retval = (-ENOSPC); /* EOM within current request */ DEBC(printk(ST_DEB_MSG "%s: EOM with %d bytes unwritten.\n", - name, transfer)); + name, count)); } else { - /* Previously buffered data not written */ - count -= do_count; - blks = do_count = 0; - STps->eof = ST_EOM_ERROR; - STps->drv_block = (-1); /* Too cautious? */ - retval = (-EIO); /* EOM for old data */ - DEBC(printk(ST_DEB_MSG - "%s: EOM with lost data.\n", - name)); + /* EOT within data buffered earlier (possible only + in fixed block mode without direct i/o) */ + if (!retry_eot && (SRpnt->sr_sense_buffer[0] & 1) == 0 && + (scode == NO_SENSE || scode == RECOVERED_ERROR)) { + move_buffer_data(STp->buffer, transfer - undone); + retry_eot = TRUE; + if (STps->drv_block >= 0) { + STps->drv_block += (transfer - undone) / + STp->block_size; + } + STps->eof = ST_EOM_OK; + DEBC(printk(ST_DEB_MSG + "%s: Retry write of %d bytes at EOM.\n", + name, STp->buffer->buffer_bytes)); + goto retry_write; + } + else { + /* Either error within data buffered by driver or + failed retry */ + count -= do_count; + blks = do_count = 0; + STps->eof = ST_EOM_ERROR; + STps->drv_block = (-1); /* Too cautious? */ + retval = (-EIO); /* EOM for old data */ + DEBC(printk(ST_DEB_MSG + "%s: EOM with lost data.\n", + name)); + } } } else { filp->f_pos -= do_count; @@ -1548,7 +1563,7 @@ static ssize_t STbp->buffer_bytes = 0; STp->dirty = 0; - if (retval) { + if (retval || retry_eot) { if (count < total) retval = total - count; goto out; @@ -1557,9 +1572,9 @@ static ssize_t if (STps->eof == ST_EOD_1) STps->eof = ST_EOM_OK; - else + else if (STps->eof != ST_EOM_OK) STps->eof = ST_NOEOF; - retval = total; + retval = total - count; out: if (SRpnt != NULL) @@ -1994,16 +2009,7 @@ static int st_set_options(Scsi_Tape *STp, long options) debugging = value; ) st_log_options(STp, STm, name); } else if (code == MT_ST_WRITE_THRESHOLD) { - value = (options & ~MT_ST_OPTIONS) * ST_KILOBYTE; - if (value < 1 || value > st_fixed_buffer_size) { - printk(KERN_WARNING - "%s: Write threshold %d too small or too large.\n", - name, value); - return (-EIO); - } - STp->write_threshold = value; - printk(KERN_INFO "%s: Write threshold set to %d bytes.\n", - name, value); + /* Retained for compatibility */ } else if (code == MT_ST_DEF_BLKSIZE) { value = (options & ~MT_ST_OPTIONS); if (value == ~MT_ST_OPTIONS) { @@ -3535,6 +3541,48 @@ static int from_buffer(ST_buffer * st_bp, char *ubp, int do_count) } +/* Move data towards start of buffer */ +static void move_buffer_data(ST_buffer * st_bp, int offset) +{ + int src_seg, dst_seg, src_offset = 0, dst_offset; + int count, total; + + if (offset == 0) + return; + + total=st_bp->buffer_bytes - offset; + for (src_seg=0; src_seg < st_bp->frp_segs; src_seg++) { + src_offset = offset; + if (src_offset < st_bp->frp[src_seg].length) + break; + offset -= st_bp->frp[src_seg].length; + } + if (src_seg == st_bp->frp_segs) { /* Should never happen */ + printk(KERN_WARNING "st: move_buffer offset overflow.\n"); + return; + } + + st_bp->buffer_bytes = st_bp->read_pointer = total; + for (dst_seg=dst_offset=0; total > 0; ) { + count = min(st_bp->frp[dst_seg].length - dst_offset, + st_bp->frp[src_seg].length - src_offset); + memmove(page_address(st_bp->frp[dst_seg].page) + dst_offset, + page_address(st_bp->frp[src_seg].page) + src_offset, count); + src_offset += count; + if (src_offset >= st_bp->frp[src_seg].length) { + src_seg++; + src_offset = 0; + } + dst_offset += count; + if (dst_offset >= st_bp->frp[dst_seg].length) { + dst_seg++; + dst_offset = 0; + } + total -= count; + } +} + + /* Fill the s/g list up to the length required for this transfer */ static void buf_to_sg(ST_buffer *STbp, unsigned int length) { @@ -3567,15 +3615,6 @@ static void validate_options(void) { if (buffer_kbs > 0) st_fixed_buffer_size = buffer_kbs * ST_KILOBYTE; - if (write_threshold_kbs > 0) - st_write_threshold = write_threshold_kbs * ST_KILOBYTE; - else if (buffer_kbs > 0) - st_write_threshold = st_fixed_buffer_size - 2048; - if (st_write_threshold > st_fixed_buffer_size) { - st_write_threshold = st_fixed_buffer_size; - printk(KERN_WARNING "st: write_threshold limited to %d bytes.\n", - st_write_threshold); - } if (max_sg_segs >= ST_FIRST_SG) st_max_sg_segs = max_sg_segs; } @@ -3592,15 +3631,20 @@ static int __init st_setup(char *str) if (ints[0] > 0) { for (i = 0; i < ints[0] && i < ARRAY_SIZE(parms); i++) - *parms[i].val = ints[i + 1]; + if (parms[i].val) + *parms[i].val = ints[i + 1]; } else { while (stp != NULL) { for (i = 0; i < ARRAY_SIZE(parms); i++) { len = strlen(parms[i].name); if (!strncmp(stp, parms[i].name, len) && (*(stp + len) == ':' || *(stp + len) == '=')) { - *parms[i].val = - simple_strtoul(stp + len + 1, NULL, 0); + if (parms[i].val) + *parms[i].val = + simple_strtoul(stp + len + 1, NULL, 0); + else + printk(KERN_WARNING "st: Obsolete parameter %s\n", + parms[i].name); break; } } @@ -3763,7 +3807,6 @@ static int st_attach(Scsi_Device * SDp) tpnt->fast_mteom = ST_FAST_MTEOM; tpnt->scsi2_logical = ST_SCSI2LOGICAL; tpnt->immediate = ST_NOWAIT; - tpnt->write_threshold = st_write_threshold; tpnt->default_drvbuffer = 0xff; /* No forced buffering */ tpnt->partition = 0; tpnt->new_partition = 0; @@ -3925,10 +3968,8 @@ static int __init init_st(void) validate_options(); printk(KERN_INFO - "st: Version %s, fixed bufsize %d, wrt %d, " - "s/g segs %d\n", - verstr, st_fixed_buffer_size, st_write_threshold, - st_max_sg_segs); + "st: Version %s, fixed bufsize %d, s/g segs %d\n", + verstr, st_fixed_buffer_size, st_max_sg_segs); if (register_chrdev(SCSI_TAPE_MAJOR, "st", &st_fops) >= 0) { if (scsi_register_device(&st_template) == 0) diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h index e2411f958828d..5540a2f10d970 100644 --- a/drivers/scsi/st.h +++ b/drivers/scsi/st.h @@ -96,7 +96,6 @@ typedef struct { unsigned char c_algo; /* compression algorithm */ unsigned char pos_unknown; /* after reset position unknown */ int tape_type; - int write_threshold; int timeout; /* timeout for normal commands */ int long_timeout; /* timeout for commands known to take long time */ diff --git a/drivers/scsi/st_options.h b/drivers/scsi/st_options.h index 19b265d978cb6..b6b5c9c376772 100644 --- a/drivers/scsi/st_options.h +++ b/drivers/scsi/st_options.h @@ -1,9 +1,9 @@ /* The compile-time configurable defaults for the Linux SCSI tape driver. - Copyright 1995-2002 Kai Makisara. + Copyright 1995-2003 Kai Makisara. - Last modified: Fri Jul 26 15:54:31 2002 by makisara + Last modified: Mon Apr 7 22:49:18 2003 by makisara */ #ifndef _ST_OPTIONS_H @@ -39,11 +39,6 @@ Must be non-zero. */ #define ST_FIXED_BUFFER_BLOCKS 32 -/* The number of kilobytes of data in the buffer that triggers an - asynchronous write in fixed block mode. See also ST_ASYNC_WRITES - below. */ -#define ST_WRITE_THRESHOLD_BLOCKS 30 - /* Maximum number of scatter/gather segments */ #define ST_MAX_SG 256 -- 2.53.0