Commit 6b38193b authored by Kai Mäkisara's avatar Kai Mäkisara Committed by Linus Torvalds

[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)
parent 69ca9ecd
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
......
......@@ -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)
......
......@@ -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 */
......
/*
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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment