summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/st.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-09-19 19:15:08 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-09-19 19:15:08 +0000
commit03ba4131783cc9e872f8bb26a03f15bc11f27564 (patch)
tree88db8dba75ae06ba3bad08e42c5e52efc162535c /drivers/scsi/st.c
parent257730f99381dd26e10b832fce4c94cae7ac1176 (diff)
- Merge with Linux 2.1.121.
- Bugfixes.
Diffstat (limited to 'drivers/scsi/st.c')
-rw-r--r--drivers/scsi/st.c497
1 files changed, 340 insertions, 157 deletions
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index a0adf83b3..cb683bbda 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -8,10 +8,10 @@
order) Klaus Ehrenfried, Wolfgang Denk, Steve Hirsch, Andreas Koppenh"ofer,
Michael Leodolter, Eyal Lebedinsky, J"org Weule, and Eric Youngdale.
- Copyright 1992 - 1997 Kai Makisara
+ Copyright 1992 - 1998 Kai Makisara
email Kai.Makisara@metla.fi
- Last modified: Wed Nov 5 23:39:52 1997 by makisara@home
+ Last modified: Sun Sep 6 09:34:49 1998 by root@home
Some small formal changes - aeb, 950809
*/
@@ -47,24 +47,27 @@
#include "scsi.h"
#include "hosts.h"
#include <scsi/scsi_ioctl.h>
+
+#define ST_KILOBYTE 1024
+
+#include "st_options.h"
#include "st.h"
+
#include "constants.h"
#ifdef MODULE
MODULE_PARM(buffer_kbs, "i");
MODULE_PARM(write_threshold_kbs, "i");
MODULE_PARM(max_buffers, "i");
+MODULE_PARM(max_sg_segs, "i");
static int buffer_kbs = 0;
static int write_threshold_kbs = 0;
static int max_buffers = 0;
+static int max_sg_segs = 0;
#endif
/* The default definitions have been moved to st_options.h */
-#define ST_KILOBYTE 1024
-
-#include "st_options.h"
-
#define ST_BUFFER_SIZE (ST_BUFFER_BLOCKS * ST_KILOBYTE)
#define ST_WRITE_THRESHOLD (ST_WRITE_THRESHOLD_BLOCKS * ST_KILOBYTE)
@@ -98,6 +101,7 @@ static ST_buffer **st_buffers;
static int st_buffer_size = ST_BUFFER_SIZE;
static int st_write_threshold = ST_WRITE_THRESHOLD;
static int st_max_buffers = ST_MAX_BUFFERS;
+static int st_max_sg_segs = ST_MAX_SG;
static Scsi_Tape * scsi_tapes = NULL;
@@ -106,6 +110,8 @@ static int modes_defined = FALSE;
static ST_buffer *new_tape_buffer(int, int);
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 int st_init(void);
static int st_attach(Scsi_Device *);
@@ -142,7 +148,12 @@ st_chk_result(Scsi_Cmnd * SCpnt)
if (!result /* && SCpnt->sense_buffer[0] == 0 */ )
return 0;
- scode = sense[2] & 0x0f;
+ if (driver_byte(result) & DRIVER_SENSE)
+ scode = sense[2] & 0x0f;
+ else {
+ sense[0] = 0; /* We don't have sense data if this byte is zero */
+ scode = 0;
+ }
#if DEBUG
if (debugging) {
@@ -172,7 +183,10 @@ st_chk_result(Scsi_Cmnd * SCpnt)
print_sense("st", SCpnt);
}
else
- printk(KERN_WARNING "st%d: Error %x.\n", dev, result);
+ printk(KERN_WARNING
+ "st%d: Error %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n",
+ dev, result, suggestion(result), driver_byte(result),
+ host_byte(result));
}
if ((sense[0] & 0x70) == 0x70 &&
@@ -246,39 +260,54 @@ st_sleep_done (Scsi_Cmnd * SCpnt)
}
-/* Do the scsi command */
+/* Do the scsi command. Waits until command performed if do_wait is true.
+ Otherwise write_behind_check() is used to check that the command
+ has finished. */
static Scsi_Cmnd *
st_do_scsi(Scsi_Cmnd *SCpnt, Scsi_Tape *STp, unsigned char *cmd, int bytes,
- int timeout, int retries)
+ int timeout, int retries, int do_wait)
{
unsigned long flags;
+ unsigned char *bp;
spin_lock_irqsave(&io_request_lock, flags);
if (SCpnt == NULL)
if ((SCpnt = scsi_allocate_device(NULL, STp->device, 1)) == NULL) {
printk(KERN_ERR "st%d: Can't get SCSI request.\n", TAPE_NR(STp->devt));
+ spin_unlock_irqrestore(&io_request_lock, flags);
return NULL;
}
cmd[1] |= (SCpnt->lun << 5) & 0xe0;
STp->sem = MUTEX_LOCKED;
+ SCpnt->use_sg = (bytes > (STp->buffer)->sg[0].length) ?
+ (STp->buffer)->use_sg : 0;
+ if (SCpnt->use_sg) {
+ bp = (char *)&((STp->buffer)->sg[0]);
+ if ((STp->buffer)->sg_segs < SCpnt->use_sg)
+ SCpnt->use_sg = (STp->buffer)->sg_segs;
+ }
+ else
+ bp = (STp->buffer)->b_data;
SCpnt->request.sem = &(STp->sem);
SCpnt->request.rq_status = RQ_SCSI_BUSY;
SCpnt->request.rq_dev = STp->devt;
- scsi_do_cmd(SCpnt, (void *)cmd, (STp->buffer)->b_data, bytes,
+ scsi_do_cmd(SCpnt, (void *)cmd, bp, bytes,
st_sleep_done, timeout, retries);
spin_unlock_irqrestore(&io_request_lock, flags);
- down(SCpnt->request.sem);
+ if (do_wait) {
+ down(SCpnt->request.sem);
- (STp->buffer)->last_result_fatal = st_chk_result(SCpnt);
+ (STp->buffer)->last_result_fatal = st_chk_result(SCpnt);
+ }
return SCpnt;
}
-/* Handle the write-behind checking */
+/* Handle the write-behind checking (downs the semaphore) */
static void
write_behind_check(Scsi_Tape *STp)
{
@@ -300,9 +329,13 @@ write_behind_check(Scsi_Tape *STp)
scsi_release_command((STp->buffer)->last_SCpnt);
if (STbuffer->writing < STbuffer->buffer_bytes)
+#if 0
memcpy(STbuffer->b_data,
STbuffer->b_data + STbuffer->writing,
STbuffer->buffer_bytes - STbuffer->writing);
+#else
+ printk(KERN_WARNING "st: write_behind_check: something left in buffer!\n");
+#endif
STbuffer->buffer_bytes -= STbuffer->writing;
STps = &(STp->ps[STp->partition]);
if (STps->drv_block >= 0) {
@@ -340,7 +373,7 @@ cross_eof(Scsi_Tape *STp, int forward)
TAPE_NR(STp->devt), forward ? "forward" : "backward");
#endif
- SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_RETRIES);
+ SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_RETRIES, TRUE);
if (!SCpnt)
return (-EBUSY);
@@ -402,7 +435,8 @@ flush_write_buffer(Scsi_Tape *STp)
cmd[3] = blks >> 8;
cmd[4] = blks;
- SCpnt = st_do_scsi(NULL, STp, cmd, transfer, STp->timeout, MAX_WRITE_RETRIES);
+ SCpnt = st_do_scsi(NULL, STp, cmd, transfer, STp->timeout, MAX_WRITE_RETRIES,
+ TRUE);
if (!SCpnt)
return (-EBUSY);
@@ -551,10 +585,9 @@ scsi_tape_open(struct inode * inode, struct file * filp)
if (dev >= st_template.dev_max || !scsi_tapes[dev].device)
return (-ENXIO);
- if( !scsi_block_when_processing_errors(scsi_tapes[dev].device) )
- {
+ if( !scsi_block_when_processing_errors(scsi_tapes[dev].device) ) {
return -ENXIO;
- }
+ }
STp = &(scsi_tapes[dev]);
if (STp->in_use) {
@@ -576,7 +609,7 @@ scsi_tape_open(struct inode * inode, struct file * filp)
}
STm = &(STp->modes[STp->current_mode]);
- /* Allocate buffer for this user */
+ /* Allocate a buffer for this user */
need_dma_buffer = STp->restr_dma;
for (i=0; i < st_nbr_buffers; i++)
if (!st_buffers[i]->in_use &&
@@ -594,6 +627,16 @@ scsi_tape_open(struct inode * inode, struct file * filp)
(STp->buffer)->in_use = 1;
(STp->buffer)->writing = 0;
(STp->buffer)->last_result_fatal = 0;
+ (STp->buffer)->use_sg = STp->device->host->sg_tablesize;
+
+ /* Compute the usable buffer size for this SCSI adapter */
+ if (!(STp->buffer)->use_sg)
+ (STp->buffer)->buffer_size = (STp->buffer)->sg[0].length;
+ else {
+ for (i=0, (STp->buffer)->buffer_size = 0; i < (STp->buffer)->use_sg &&
+ i < (STp->buffer)->sg_segs; i++)
+ (STp->buffer)->buffer_size += (STp->buffer)->sg[i].length;
+ }
flags = filp->f_flags;
STp->write_prot = ((flags & O_ACCMODE) == O_RDONLY);
@@ -617,7 +660,8 @@ scsi_tape_open(struct inode * inode, struct file * filp)
memset ((void *) &cmd[0], 0, 10);
cmd[0] = TEST_UNIT_READY;
- SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->long_timeout, MAX_READY_RETRIES);
+ SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->long_timeout, MAX_READY_RETRIES,
+ TRUE);
if (!SCpnt) {
if (scsi_tapes[dev].device->host->hostt->module)
__MOD_DEC_USE_COUNT(scsi_tapes[dev].device->host->hostt->module);
@@ -631,7 +675,8 @@ scsi_tape_open(struct inode * inode, struct file * filp)
memset ((void *) &cmd[0], 0, 10);
cmd[0] = TEST_UNIT_READY;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, 0, STp->long_timeout, MAX_READY_RETRIES);
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, 0, STp->long_timeout, MAX_READY_RETRIES,
+ TRUE);
(STp->device)->was_reset = 0;
STp->partition = STp->new_partition = 0;
@@ -675,7 +720,7 @@ scsi_tape_open(struct inode * inode, struct file * filp)
memset ((void *) &cmd[0], 0, 10);
cmd[0] = READ_BLOCK_LIMITS;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, 6, STp->timeout, MAX_READY_RETRIES);
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, 6, STp->timeout, MAX_READY_RETRIES, TRUE);
if (!SCpnt->result && !SCpnt->sense_buffer[0]) {
STp->max_block = ((STp->buffer)->b_data[1] << 16) |
@@ -701,7 +746,7 @@ scsi_tape_open(struct inode * inode, struct file * filp)
cmd[0] = MODE_SENSE;
cmd[4] = 12;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, 12, STp->timeout, MAX_READY_RETRIES);
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, 12, STp->timeout, MAX_READY_RETRIES, TRUE);
if ((STp->buffer)->last_result_fatal != 0) {
#if DEBUG
@@ -754,7 +799,7 @@ scsi_tape_open(struct inode * inode, struct file * filp)
SCpnt = NULL;
if (STp->block_size > 0)
- (STp->buffer)->buffer_blocks = st_buffer_size / STp->block_size;
+ (STp->buffer)->buffer_blocks = (STp->buffer)->buffer_size / STp->block_size;
else
(STp->buffer)->buffer_blocks = 1;
(STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
@@ -830,9 +875,9 @@ scsi_tape_open(struct inode * inode, struct file * filp)
}
-/* Close the device*/
+/* Flush the tape buffer before close */
static int
-scsi_tape_close(struct inode * inode, struct file * filp)
+scsi_tape_flush(struct file * filp)
{
int result = 0, result2;
static unsigned char cmd[10];
@@ -841,6 +886,7 @@ scsi_tape_close(struct inode * inode, struct file * filp)
ST_mode * STm;
ST_partstat * STps;
+ struct inode *inode = filp->f_dentry->d_inode;
kdev_t devt = inode->i_rdev;
int dev;
@@ -877,7 +923,8 @@ scsi_tape_close(struct inode * inode, struct file * filp)
cmd[0] = WRITE_FILEMARKS;
cmd[4] = 1 + STp->two_fm;
- SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_WRITE_RETRIES);
+ SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_WRITE_RETRIES,
+ TRUE);
if (!SCpnt)
goto out;
@@ -947,6 +994,23 @@ out:
result = result2;
}
+ return result;
+}
+
+
+/* Close the device and release it */
+ static int
+scsi_tape_close(struct inode * inode, struct file * filp)
+{
+ int result = 0;
+ Scsi_Tape * STp;
+
+ kdev_t devt = inode->i_rdev;
+ int dev;
+
+ dev = TAPE_NR(devt);
+ STp = &(scsi_tapes[dev]);
+
if (STp->door_locked == ST_LOCKED_AUTO)
st_int_ioctl(inode, MTUNLOCK, 0);
@@ -981,7 +1045,6 @@ st_write(struct file * filp, const char * buf, size_t count, loff_t *ppos)
ST_mode * STm;
ST_partstat * STps;
int dev = TAPE_NR(inode->i_rdev);
- unsigned long flags;
STp = &(scsi_tapes[dev]);
@@ -991,10 +1054,9 @@ st_write(struct file * filp, const char * buf, size_t count, loff_t *ppos)
* may try and take the device offline, in which case all further
* access to the device is prohibited.
*/
- if( !scsi_block_when_processing_errors(STp->device) )
- {
+ if( !scsi_block_when_processing_errors(STp->device) ) {
return -ENXIO;
- }
+ }
if (ppos != &filp->f_pos) {
/* "A request was outside the capabilities of the device." */
@@ -1027,6 +1089,10 @@ st_write(struct file * filp, const char * buf, size_t count, loff_t *ppos)
}
#endif
+ /* Write must be integral number of blocks */
+ if (STp->block_size != 0 && (count % STp->block_size) != 0)
+ return (-EIO);
+
if (STp->can_partitions &&
(retval = update_partition(inode)) < 0)
return retval;
@@ -1092,8 +1158,10 @@ st_write(struct file * filp, const char * buf, size_t count, loff_t *ppos)
return (-EFAULT);
if (!STm->do_buffer_writes) {
+#if 0
if (STp->block_size != 0 && (count % STp->block_size) != 0)
return (-EIO); /* Write must be integral number of blocks */
+#endif
write_threshold = 1;
}
else
@@ -1110,9 +1178,9 @@ st_write(struct file * filp, const char * buf, size_t count, loff_t *ppos)
STps->rw = ST_WRITING;
b_point = buf;
- while((STp->block_size == 0 && !STm->do_async_writes && count > 0) ||
- (STp->block_size != 0 &&
- (STp->buffer)->buffer_bytes + count > write_threshold))
+ while ((STp->block_size == 0 && !STm->do_async_writes && count > 0) ||
+ (STp->block_size != 0 &&
+ (STp->buffer)->buffer_bytes + count > write_threshold))
{
doing_write = 1;
if (STp->block_size == 0)
@@ -1124,21 +1192,19 @@ st_write(struct file * filp, const char * buf, size_t count, loff_t *ppos)
do_count = count;
}
- i = copy_from_user((STp->buffer)->b_data +
- (STp->buffer)->buffer_bytes, b_point, do_count);
+ i = append_to_buffer(b_point, STp->buffer, do_count);
if (i) {
- if (SCpnt != NULL)
- {
+ if (SCpnt != NULL) {
scsi_release_command(SCpnt);
SCpnt = NULL;
- }
- return (-EFAULT);
+ }
+ return i;
}
if (STp->block_size == 0)
blks = transfer = do_count;
else {
- blks = ((STp->buffer)->buffer_bytes + do_count) /
+ blks = (STp->buffer)->buffer_bytes /
STp->block_size;
transfer = blks * STp->block_size;
}
@@ -1146,7 +1212,8 @@ st_write(struct file * filp, const char * buf, size_t count, loff_t *ppos)
cmd[3] = blks >> 8;
cmd[4] = blks;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, transfer, STp->timeout, MAX_WRITE_RETRIES);
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, transfer, STp->timeout,
+ MAX_WRITE_RETRIES, TRUE);
if (!SCpnt)
return (-EBUSY);
@@ -1223,18 +1290,15 @@ st_write(struct file * filp, const char * buf, size_t count, loff_t *ppos)
}
if (count != 0) {
STp->dirty = 1;
- i = copy_from_user((STp->buffer)->b_data +
- (STp->buffer)->buffer_bytes, b_point, count);
+ i = append_to_buffer(b_point, STp->buffer, count);
if (i) {
- if (SCpnt != NULL)
- {
+ if (SCpnt != NULL) {
scsi_release_command(SCpnt);
SCpnt = NULL;
- }
- return (-EFAULT);
+ }
+ return i;
}
filp->f_pos += count;
- (STp->buffer)->buffer_bytes += count;
count = 0;
}
@@ -1249,11 +1313,6 @@ st_write(struct file * filp, const char * buf, size_t count, loff_t *ppos)
(STp->buffer)->buffer_bytes >= STp->block_size) ||
STp->block_size == 0) ) {
/* Schedule an asynchronous write */
- if (!SCpnt) {
- SCpnt = scsi_allocate_device(NULL, STp->device, 1);
- if (!SCpnt)
- return (-EBUSY);
- }
if (STp->block_size == 0)
(STp->buffer)->writing = (STp->buffer)->buffer_bytes;
else
@@ -1269,26 +1328,19 @@ st_write(struct file * filp, const char * buf, size_t count, loff_t *ppos)
cmd[2] = blks >> 16;
cmd[3] = blks >> 8;
cmd[4] = blks;
- STp->sem = MUTEX_LOCKED;
- SCpnt->request.sem = &(STp->sem);
- SCpnt->request.rq_status = RQ_SCSI_BUSY;
- SCpnt->request.rq_dev = STp->devt;
#if DEBUG
STp->write_pending = 1;
#endif
- spin_lock_irqsave(&io_request_lock, flags);
- scsi_do_cmd (SCpnt,
- (void *) cmd, (STp->buffer)->b_data,
- (STp->buffer)->writing,
- st_sleep_done, STp->timeout, MAX_WRITE_RETRIES);
- spin_unlock_irqrestore(&io_request_lock, flags);
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, (STp->buffer)->writing, STp->timeout,
+ MAX_WRITE_RETRIES, FALSE);
+ if (SCpnt == NULL)
+ return (-EIO);
}
- else if (SCpnt != NULL)
- {
+ else if (SCpnt != NULL) {
scsi_release_command(SCpnt);
SCpnt = NULL;
- }
+ }
STps->at_sm &= (total == 0);
if (total > 0)
STps->eof = ST_NOEOF;
@@ -1343,7 +1395,7 @@ read_tape(struct inode *inode, long count, Scsi_Cmnd **aSCpnt)
cmd[4] = blks;
SCpnt = *aSCpnt;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, bytes, STp->timeout, MAX_RETRIES);
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, bytes, STp->timeout, MAX_RETRIES, TRUE);
*aSCpnt = SCpnt;
if (!SCpnt)
return (-EBUSY);
@@ -1351,7 +1403,6 @@ read_tape(struct inode *inode, long count, Scsi_Cmnd **aSCpnt)
(STp->buffer)->read_pointer = 0;
STps->at_sm = 0;
-
/* Something to check */
if ((STp->buffer)->last_result_fatal) {
retval = 1;
@@ -1507,10 +1558,9 @@ st_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
* may try and take the device offline, in which case all further
* access to the device is prohibited.
*/
- if( !scsi_block_when_processing_errors(STp->device) )
- {
+ if( !scsi_block_when_processing_errors(STp->device) ) {
return -ENXIO;
- }
+ }
if (ppos != &filp->f_pos) {
/* "A request was outside the capabilities of the device." */
@@ -1590,10 +1640,9 @@ st_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
if ((STp->buffer)->buffer_bytes == 0) {
special = read_tape(inode, count - total, &SCpnt);
if (special < 0) { /* No need to continue read */
- if (SCpnt != NULL)
- {
+ if (SCpnt != NULL) {
scsi_release_command(SCpnt);
- }
+ }
return special;
}
}
@@ -1607,21 +1656,17 @@ st_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
#endif
transfer = (STp->buffer)->buffer_bytes < count - total ?
(STp->buffer)->buffer_bytes : count - total;
- i = copy_to_user(buf, (STp->buffer)->b_data +
- (STp->buffer)->read_pointer, transfer);
+ i = from_buffer(STp->buffer, buf, transfer);
if (i) {
- if (SCpnt != NULL)
- {
+ if (SCpnt != NULL) {
scsi_release_command(SCpnt);
SCpnt = NULL;
- }
- return (-EFAULT);
+ }
+ return i;
}
filp->f_pos += transfer;
buf += transfer;
total += transfer;
- (STp->buffer)->buffer_bytes -= transfer;
- (STp->buffer)->read_pointer += transfer;
}
if (STp->block_size == 0)
@@ -1629,11 +1674,10 @@ st_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
} /* for (total = 0, special = 0; total < count && !special; ) */
- if (SCpnt != NULL)
- {
+ if (SCpnt != NULL) {
scsi_release_command(SCpnt);
SCpnt = NULL;
- }
+ }
/* Change the eof state if no data from tape or buffer */
if (total == 0) {
@@ -1867,7 +1911,7 @@ st_compression(Scsi_Tape * STp, int state)
cmd[2] = COMPRESSION_PAGE;
cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0);
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
if (SCpnt == NULL)
return (-EBUSY);
dev = TAPE_NR(SCpnt->request.rq_dev);
@@ -1912,7 +1956,7 @@ st_compression(Scsi_Tape * STp, int state)
(STp->buffer)->b_data[0] = 0; /* Reserved data length */
(STp->buffer)->b_data[1] = 0; /* Reserved media type byte */
(STp->buffer)->b_data[MODE_HEADER_LENGTH] &= 0x3f;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0);
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
if ((STp->buffer)->last_result_fatal != 0) {
#if DEBUG
@@ -2298,7 +2342,7 @@ st_int_ioctl(struct inode * inode,
return (-ENOSYS);
}
- SCpnt = st_do_scsi(NULL, STp, cmd, datalen, timeout, MAX_RETRIES);
+ SCpnt = st_do_scsi(NULL, STp, cmd, datalen, timeout, MAX_RETRIES, TRUE);
if (!SCpnt)
return (-EBUSY);
@@ -2472,7 +2516,7 @@ get_location(struct inode * inode, unsigned int *block, int *partition,
if (!logical && !STp->scsi2_logical)
scmd[1] = 1;
}
- SCpnt = st_do_scsi(NULL, STp, scmd, 20, STp->timeout, MAX_READY_RETRIES);
+ SCpnt = st_do_scsi(NULL, STp, scmd, 20, STp->timeout, MAX_READY_RETRIES, TRUE);
if (!SCpnt)
return (-EBUSY);
@@ -2596,7 +2640,7 @@ set_location(struct inode * inode, unsigned int block, int partition,
timeout = STp->timeout;
#endif
- SCpnt = st_do_scsi(NULL, STp, scmd, 20, timeout, MAX_READY_RETRIES);
+ SCpnt = st_do_scsi(NULL, STp, scmd, 20, timeout, MAX_READY_RETRIES, TRUE);
if (!SCpnt)
return (-EBUSY);
@@ -2692,7 +2736,7 @@ nbr_partitions(struct inode * inode)
cmd[2] = PART_PAGE;
cmd[4] = 200;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, 200, STp->timeout, MAX_READY_RETRIES);
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, 200, STp->timeout, MAX_READY_RETRIES, TRUE);
if (SCpnt == NULL)
return (-EBUSY);
scsi_release_command(SCpnt);
@@ -2767,7 +2811,8 @@ partition_tape(struct inode * inode, int size)
cmd[1] = 0x10;
cmd[4] = length + MODE_HEADER_LENGTH;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->long_timeout, MAX_READY_RETRIES);
+ SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->long_timeout,
+ MAX_READY_RETRIES, TRUE);
if (SCpnt == NULL)
return (-EBUSY);
scsi_release_command(SCpnt);
@@ -2815,10 +2860,9 @@ st_ioctl(struct inode * inode,struct file * file,
* may try and take the device offline, in which case all further
* access to the device is prohibited.
*/
- if( !scsi_block_when_processing_errors(STp->device) )
- {
+ if( !scsi_block_when_processing_errors(STp->device) ) {
return -ENXIO;
- }
+ }
cmd_type = _IOC_TYPE(cmd_in);
cmd_nr = _IOC_NR(cmd_in);
@@ -3034,29 +3078,69 @@ st_ioctl(struct inode * inode,struct file * file,
static ST_buffer *
new_tape_buffer( int from_initialization, int need_dma )
{
- int priority, a_size;
+ int i, priority, b_size, got = 0, segs = 0;
ST_buffer *tb;
if (st_nbr_buffers >= st_template.dev_max)
return NULL; /* Should never happen */
- if (from_initialization) {
+ if (from_initialization)
priority = GFP_ATOMIC;
- a_size = st_buffer_size;
- }
- else {
+ else
priority = GFP_KERNEL;
- for (a_size = PAGE_SIZE; a_size < st_buffer_size; a_size <<= 1)
- ; /* Make sure we allocate efficiently */
- }
- tb = (ST_buffer *)scsi_init_malloc(sizeof(ST_buffer), priority);
+
+ i = sizeof(ST_buffer) + (st_max_sg_segs - 1) * sizeof(struct scatterlist);
+ tb = (ST_buffer *)scsi_init_malloc(i, priority);
if (tb) {
+ tb->this_size = i;
if (need_dma)
priority |= GFP_DMA;
- tb->b_data = (unsigned char *)scsi_init_malloc(a_size, priority);
- if (!tb->b_data) {
- scsi_init_free((char *)tb, sizeof(ST_buffer));
- tb = NULL;
+
+ /* Try to allocate the first segment up to ST_FIRST_ORDER and the
+ others big enough to reach the goal */
+ for (b_size = PAGE_SIZE << ST_FIRST_ORDER;
+ b_size / 2 >= st_buffer_size && b_size > PAGE_SIZE; )
+ b_size /= 2;
+ for ( ; b_size >= PAGE_SIZE; b_size /= 2) {
+ tb->sg[0].address =
+ (unsigned char *)scsi_init_malloc(b_size, priority);
+ if (tb->sg[0].address != NULL) {
+ tb->sg[0].alt_address = NULL;
+ tb->sg[0].length = b_size;
+ break;
+ }
+ }
+ if (tb->sg[segs].address == NULL) {
+ scsi_init_free((char *)tb, tb->this_size);
+ tb = NULL;
+ }
+ else { /* Got something, continue */
+
+ for (b_size = PAGE_SIZE;
+ st_buffer_size > tb->sg[0].length + (ST_FIRST_SG - 1) * b_size; )
+ b_size *= 2;
+
+ for (segs=1, got=tb->sg[0].length;
+ got < st_buffer_size && segs < ST_FIRST_SG; ) {
+ tb->sg[segs].address =
+ (unsigned char *)scsi_init_malloc(b_size, priority);
+ if (tb->sg[segs].address == NULL) {
+ if (st_buffer_size - got <=
+ (ST_FIRST_SG - segs) * b_size / 2) {
+ b_size /= 2; /* Large enough for the rest of the buffers */
+ continue;
+ }
+ for (i=0; i < segs - 1; i++)
+ scsi_init_free(tb->sg[i].address, tb->sg[i].length);
+ scsi_init_free((char *)tb, tb->this_size);
+ tb = NULL;
+ break;
+ }
+ tb->sg[segs].alt_address = NULL;
+ tb->sg[segs].length = b_size;
+ got += b_size;
+ segs++;
+ }
}
}
if (!tb) {
@@ -3064,18 +3148,25 @@ new_tape_buffer( int from_initialization, int need_dma )
st_nbr_buffers);
return NULL;
}
+ tb->sg_segs = tb->orig_sg_segs = segs;
+ tb->b_data = tb->sg[0].address;
+
#if DEBUG
- if (debugging)
+ if (debugging) {
+ printk(ST_DEB_MSG
+ "st: Allocated tape buffer %d (%d bytes, %d segments, dma: %d, a: %p).\n",
+ st_nbr_buffers, got, tb->sg_segs, need_dma, tb->b_data);
printk(ST_DEB_MSG
- "st: Allocated tape buffer %d (%d bytes, dma: %d, a: %p).\n",
- st_nbr_buffers, a_size, need_dma, tb->b_data);
+ "st: segment sizes: first %d, last %d bytes.\n",
+ tb->sg[0].length, tb->sg[segs-1].length);
+ }
#endif
tb->in_use = 0;
tb->dma = need_dma;
- tb->buffer_size = a_size;
+ tb->buffer_size = got;
tb->writing = 0;
- tb->orig_b_data = NULL;
st_buffers[st_nbr_buffers++] = tb;
+
return tb;
}
@@ -3084,31 +3175,51 @@ new_tape_buffer( int from_initialization, int need_dma )
static int
enlarge_buffer(ST_buffer *STbuffer, int new_size, int need_dma)
{
- int a_size, priority;
- unsigned char *tbd;
+ int segs, nbr, max_segs, b_size, priority, got;
normalize_buffer(STbuffer);
- for (a_size = PAGE_SIZE; a_size < new_size; a_size <<= 1)
- ; /* Make sure that we allocate efficiently */
+ max_segs = STbuffer->use_sg;
+ if (max_segs > st_max_sg_segs)
+ max_segs = st_max_sg_segs;
+ nbr = max_segs - STbuffer->sg_segs;
+ if (nbr <= 0)
+ return FALSE;
priority = GFP_KERNEL;
if (need_dma)
priority |= GFP_DMA;
- tbd = (unsigned char *)scsi_init_malloc(a_size, priority);
- if (!tbd)
- return FALSE;
+ for (b_size = PAGE_SIZE; b_size * nbr < new_size - STbuffer->buffer_size; )
+ b_size *= 2;
+
+ for (segs=STbuffer->sg_segs, got=STbuffer->buffer_size;
+ segs < max_segs && got < new_size; ) {
+ STbuffer->sg[segs].address =
+ (unsigned char *)scsi_init_malloc(b_size, priority);
+ if (STbuffer->sg[segs].address == NULL) {
+ if (new_size - got <= (max_segs - segs) * b_size / 2) {
+ b_size /= 2; /* Large enough for the rest of the buffers */
+ continue;
+ }
+ printk(KERN_NOTICE "st: failed to enlarge buffer to %d bytes.\n",
+ new_size);
+ normalize_buffer(STbuffer);
+ return FALSE;
+ }
+ STbuffer->sg[segs].alt_address = NULL;
+ STbuffer->sg[segs].length = b_size;
+ STbuffer->sg_segs += 1;
+ got += b_size;
+ STbuffer->buffer_size = got;
+ segs++;
+ }
#if DEBUG
if (debugging)
- printk(ST_DEB_MSG
- "st: Buffer at %p enlarged to %d bytes (dma: %d, a: %p).\n",
- STbuffer->b_data, a_size, need_dma, tbd);
+ printk(ST_DEB_MSG
+ "st: Succeeded to enlarge buffer to %d bytes (segs %d->%d, %d).\n",
+ got, STbuffer->orig_sg_segs, STbuffer->sg_segs, b_size);
#endif
- STbuffer->orig_b_data = STbuffer->b_data;
- STbuffer->orig_size = STbuffer->buffer_size;
- STbuffer->b_data = tbd;
- STbuffer->buffer_size = a_size;
return TRUE;
}
@@ -3117,19 +3228,87 @@ enlarge_buffer(ST_buffer *STbuffer, int new_size, int need_dma)
static void
normalize_buffer(ST_buffer *STbuffer)
{
- if (STbuffer->orig_b_data == NULL)
- return;
-
- scsi_init_free(STbuffer->b_data, STbuffer->buffer_size);
- STbuffer->b_data = STbuffer->orig_b_data;
- STbuffer->orig_b_data = NULL;
- STbuffer->buffer_size = STbuffer->orig_size;
+ int i;
+ for (i=STbuffer->orig_sg_segs; i < STbuffer->sg_segs; i++) {
+ scsi_init_free(STbuffer->sg[i].address, STbuffer->sg[i].length);
+ STbuffer->buffer_size -= STbuffer->sg[i].length;
+ }
#if DEBUG
- if (debugging)
- printk(ST_DEB_MSG "st: Buffer at %p normalized to %d bytes.\n",
- STbuffer->b_data, STbuffer->buffer_size);
+ if (debugging && STbuffer->orig_sg_segs < STbuffer->sg_segs)
+ printk(ST_DEB_MSG "st: Buffer at %p normalized to %d bytes (segs %d).\n",
+ STbuffer->b_data, STbuffer->buffer_size, STbuffer->sg_segs);
#endif
+ STbuffer->sg_segs = STbuffer->orig_sg_segs;
+}
+
+
+/* Move data from the user buffer to the tape buffer. Returns zero (success) or
+ negative error code. */
+ static int
+append_to_buffer(const char *ubp, ST_buffer *st_bp, int do_count)
+{
+ int i, cnt, res, offset;
+
+ for (i=0, offset=st_bp->buffer_bytes;
+ i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
+ offset -= st_bp->sg[i].length;
+ if (i == st_bp->sg_segs) { /* Should never happen */
+ printk(KERN_WARNING "st: append_to_buffer offset overflow.\n");
+ return (-EIO);
+ }
+ for ( ; i < st_bp->sg_segs && do_count > 0; i++) {
+ cnt = st_bp->sg[i].length - offset < do_count ?
+ st_bp->sg[i].length - offset : do_count;
+ res = copy_from_user(st_bp->sg[i].address + offset, ubp, cnt);
+ if (res)
+ return (-EFAULT);
+ do_count -= cnt;
+ st_bp->buffer_bytes += cnt;
+ ubp += cnt;
+ offset = 0;
+ }
+ if (do_count) { /* Should never happen */
+ printk(KERN_WARNING "st: append_to_buffer overflow (left %d).\n",
+ do_count);
+ return (-EIO);
+ }
+ return 0;
+}
+
+
+/* Move data from the tape buffer to the user buffer. Returns zero (success) or
+ negative error code. */
+ static int
+from_buffer(ST_buffer *st_bp, char *ubp, int do_count)
+{
+ int i, cnt, res, offset;
+
+ for (i=0, offset=st_bp->read_pointer;
+ i < st_bp->sg_segs && offset >= st_bp->sg[i].length; i++)
+ offset -= st_bp->sg[i].length;
+ if (i == st_bp->sg_segs) { /* Should never happen */
+ printk(KERN_WARNING "st: from_buffer offset overflow.\n");
+ return (-EIO);
+ }
+ for ( ; i < st_bp->sg_segs && do_count > 0; i++) {
+ cnt = st_bp->sg[i].length - offset < do_count ?
+ st_bp->sg[i].length - offset : do_count;
+ res = copy_to_user(ubp, st_bp->sg[i].address + offset, cnt);
+ if (res)
+ return (-EFAULT);
+ do_count -= cnt;
+ st_bp->buffer_bytes -= cnt;
+ st_bp->read_pointer += cnt;
+ ubp += cnt;
+ offset = 0;
+ }
+ if (do_count) { /* Should never happen */
+ printk(KERN_WARNING "st: from_buffer overflow (left %d).\n",
+ do_count);
+ return (-EIO);
+ }
+ return 0;
}
@@ -3162,6 +3341,7 @@ static struct file_operations st_fops = {
st_ioctl, /* ioctl */
NULL, /* mmap */
scsi_tape_open, /* open */
+ scsi_tape_flush, /* flush */
scsi_tape_close, /* release */
NULL /* fsync */
};
@@ -3172,13 +3352,13 @@ static int st_attach(Scsi_Device * SDp){
ST_partstat * STps;
int i;
- if(SDp->type != TYPE_TAPE) return 1;
+ if (SDp->type != TYPE_TAPE)
+ return 1;
- if(st_template.nr_dev >= st_template.dev_max)
- {
- SDp->attached--;
- return 1;
- }
+ if (st_template.nr_dev >= st_template.dev_max) {
+ SDp->attached--;
+ return 1;
+ }
for(tpnt = scsi_tapes, i=0; i<st_template.dev_max; i++, tpnt++)
if(!tpnt->device) break;
@@ -3249,7 +3429,7 @@ static int st_detect(Scsi_Device * SDp)
{
if(SDp->type != TYPE_TAPE) return 0;
- printk(KERN_INFO
+ printk(KERN_WARNING
"Detected scsi tape st%d at scsi%d, channel %d, id %d, lun %d\n",
st_template.dev_noticed++,
SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
@@ -3375,11 +3555,6 @@ static void st_detach(Scsi_Device * SDp)
int init_module(void) {
int result;
- st_template.module = &__this_module;
- result = scsi_register_module(MODULE_SCSI_DEV, &st_template);
- if (result)
- return result;
-
if (buffer_kbs > 0)
st_buffer_size = buffer_kbs * ST_KILOBYTE;
if (write_threshold_kbs > 0)
@@ -3388,15 +3563,22 @@ int init_module(void) {
st_write_threshold = st_buffer_size;
if (max_buffers > 0)
st_max_buffers = max_buffers;
-printk(KERN_INFO "st: bufsize %d, wrt %d, max buffers %d.\n",
-st_buffer_size, st_write_threshold, st_max_buffers);
+ if (max_sg_segs >= ST_FIRST_SG)
+ st_max_sg_segs = max_sg_segs;
+ printk(KERN_INFO "st: bufsize %d, wrt %d, max buffers %d, s/g segs %d.\n",
+ st_buffer_size, st_write_threshold, st_max_buffers, st_max_sg_segs);
+
+ st_template.module = &__this_module;
+ result = scsi_register_module(MODULE_SCSI_DEV, &st_template);
+ if (result)
+ return result;
return 0;
}
void cleanup_module( void)
{
- int i;
+ int i, j;
scsi_unregister_module(MODULE_SCSI_DEV, &st_template);
unregister_chrdev(SCSI_TAPE_MAJOR, "st");
@@ -3408,9 +3590,10 @@ void cleanup_module( void)
if (st_buffers != NULL) {
for (i=0; i < st_nbr_buffers; i++)
if (st_buffers[i] != NULL) {
- scsi_init_free((char *) st_buffers[i]->b_data,
- st_buffers[i]->buffer_size);
- scsi_init_free((char *) st_buffers[i], sizeof(ST_buffer));
+ for (j=0; j < st_buffers[i]->sg_segs; j++)
+ scsi_init_free((char *) st_buffers[i]->sg[j].address,
+ st_buffers[i]->sg[j].length);
+ scsi_init_free((char *) st_buffers[i], st_buffers[i]->this_size);
}
scsi_init_free((char *) st_buffers,