]> git.hungrycats.org Git - linux/commitdiff
[PATCH] SCSI tape EOT write fixes
authorKai Mäkisara <kai.makisara@kolumbus.fi>
Mon, 14 Apr 2003 03:33:35 +0000 (20:33 -0700)
committerLinus Torvalds <torvalds@home.transmeta.com>
Mon, 14 Apr 2003 03:33:35 +0000 (20:33 -0700)
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
drivers/scsi/st.c
drivers/scsi/st.h
drivers/scsi/st_options.h

index 30b9c3955c1f3b62086878324835fcc8d49176d8..39f0380b4582705e45c211e754c674101464e466 100644 (file)
@@ -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
index f8917661cd13c85160643f6a744d480bada27589..00f5fb6bea0a9aff5a4472e332ccbb422a5d9e50 100644 (file)
@@ -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 <rgooch@atnf.csiro.au> Devfs support
  */
 
-static char *verstr = "20021214";
+static char *verstr = "20030412";
 
 #include <linux/module.h>
 
@@ -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)
index e2411f958828d613f75cb42702f1f49b9b141673..5540a2f10d970a4910e431bab57f69c7f56e45a8 100644 (file)
@@ -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 */
 
index 19b265d978cb6da2e14d0970ed822f27364f7852..b6b5c9c376772c414829bb45b5c96fea7490e7eb 100644 (file)
@@ -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
    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