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>
#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;
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");
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;
{
"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
/* 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. */
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;
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,
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;
STps->rw = ST_WRITING;
b_point = buf;
- while (count > 0) {
+ while (count > 0 && !retry_eot) {
if (STbp->do_dio) {
do_count = count;
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 &&
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;
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)
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;
(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;
STbp->buffer_bytes = 0;
STp->dirty = 0;
- if (retval) {
+ if (retval || retry_eot) {
if (count < total)
retval = total - count;
goto out;
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)
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) {
}
+/* 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)
{
{
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;
}
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;
}
}
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;
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)