patch-2.4.20 linux-2.4.20/drivers/scsi/st.c
Next file: linux-2.4.20/drivers/scsi/st.h
Previous file: linux-2.4.20/drivers/scsi/sr_ioctl.c
Back to the patch index
Back to the overall index
- Lines: 475
- Date:
Thu Nov 28 15:53:14 2002
- Orig file:
linux-2.4.19/drivers/scsi/st.c
- Orig date:
Mon Feb 25 11:38:04 2002
diff -urN linux-2.4.19/drivers/scsi/st.c linux-2.4.20/drivers/scsi/st.c
@@ -12,7 +12,7 @@
Copyright 1992 - 2002 Kai Makisara
email Kai.Makisara@metla.fi
- Last modified: Tue Feb 5 21:25:55 2002 by makisara
+ Last modified: Mon Aug 5 22:54:13 2002 by makisara
Some small formal changes - aeb, 950809
Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
@@ -21,7 +21,7 @@
error handling will be discarded.
*/
-static char *verstr = "20020205";
+static char *verstr = "20020805";
#include <linux/module.h>
@@ -75,6 +75,8 @@
static int write_threshold_kbs;
static int max_buffers = (-1);
static int max_sg_segs;
+static int blocking_open = ST_BLOCKING_OPEN;
+
MODULE_AUTHOR("Kai Makisara");
MODULE_DESCRIPTION("SCSI Tape Driver");
@@ -88,6 +90,8 @@
MODULE_PARM_DESC(max_buffers, "Maximum number of buffer allocated at initialisation (4)");
MODULE_PARM(max_sg_segs, "i");
MODULE_PARM_DESC(max_sg_segs, "Maximum number of scatter/gather segments to use (32)");
+MODULE_PARM(blocking_open, "i");
+MODULE_PARM_DESC(blocking_open, "Block in open if not ready an no O_NONBLOCK (0)");
EXPORT_NO_SYMBOLS;
@@ -107,6 +111,9 @@
},
{
"max_sg_segs", &max_sg_segs
+ },
+ {
+ "blocking_open", &blocking_open
}
};
#endif
@@ -157,6 +164,7 @@
static ST_buffer *new_tape_buffer(int, int, int);
static int enlarge_buffer(ST_buffer *, int, int);
static void normalize_buffer(ST_buffer *);
+static int set_sg_lengths(ST_buffer *, unsigned int);
static int append_to_buffer(const char *, ST_buffer *, int);
static int from_buffer(ST_buffer *, char *, int);
@@ -239,7 +247,7 @@
return 0;
}
- if (driver_byte(result) & DRIVER_SENSE)
+ if ((driver_byte(result) & DRIVER_SENSE) == DRIVER_SENSE)
scode = sense[2] & 0x0f;
else {
sense[0] = 0;
@@ -378,12 +386,11 @@
if (SRpnt->sr_device->scsi_level <= SCSI_2)
cmd[1] |= (SRpnt->sr_device->lun << 5) & 0xe0;
init_completion(&STp->wait);
- SRpnt->sr_use_sg = (bytes > (STp->buffer)->sg[0].length) ?
+ SRpnt->sr_use_sg = (bytes > (STp->buffer)->sg_lengths[0]) ?
(STp->buffer)->use_sg : 0;
if (SRpnt->sr_use_sg) {
bp = (char *) &((STp->buffer)->sg[0]);
- if ((STp->buffer)->sg_segs < SRpnt->sr_use_sg)
- SRpnt->sr_use_sg = (STp->buffer)->sg_segs;
+ SRpnt->sr_use_sg = set_sg_lengths(STp->buffer, bytes);
} else
bp = (STp->buffer)->b_data;
SRpnt->sr_data_direction = direction;
@@ -638,20 +645,97 @@
return 0;
}
+/* Test if the drive is ready. Returns either one of the codes below or a negative system
+ error code. */
+#define CHKRES_READY 0
+#define CHKRES_NEW_SESSION 1
+#define CHKRES_NOT_READY 2
+#define CHKRES_NO_TAPE 3
+
+#define MAX_ATTENTIONS 10
+
+static int test_ready(Scsi_Tape *STp, int do_wait)
+{
+ int attentions, waits, max_wait, scode;
+ int retval = CHKRES_READY, new_session = FALSE;
+ unsigned char cmd[MAX_COMMAND_SIZE];
+ Scsi_Request *SRpnt = NULL;
+
+ max_wait = do_wait ? ST_BLOCK_SECONDS : 0;
+
+ for (attentions=waits=0; ; ) {
+ memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
+ cmd[0] = TEST_UNIT_READY;
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, 0, SCSI_DATA_NONE,
+ STp->long_timeout, MAX_READY_RETRIES, TRUE);
+
+ if (!SRpnt) {
+ retval = (STp->buffer)->syscall_result;
+ break;
+ }
+
+ if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70) {
+
+ scode = (SRpnt->sr_sense_buffer[2] & 0x0f);
+
+ if (scode == UNIT_ATTENTION) { /* New media? */
+ new_session = TRUE;
+ if (attentions < MAX_ATTENTIONS) {
+ attentions++;
+ continue;
+ }
+ else {
+ retval = (-EIO);
+ break;
+ }
+ }
+
+ if (scode == NOT_READY) {
+ if (waits < max_wait) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+ if (signal_pending(current)) {
+ retval = (-EINTR);
+ break;
+ }
+ waits++;
+ continue;
+ }
+ else {
+ if ((STp->device)->scsi_level >= SCSI_2 &&
+ SRpnt->sr_sense_buffer[12] == 0x3a) /* Check ASC */
+ retval = CHKRES_NO_TAPE;
+ else
+ retval = CHKRES_NOT_READY;
+ break;
+ }
+ }
+ }
+
+ retval = (STp->buffer)->syscall_result;
+ if (!retval)
+ retval = new_session ? CHKRES_NEW_SESSION : CHKRES_READY;
+ break;
+ }
+
+ if (SRpnt != NULL)
+ scsi_release_request(SRpnt);
+ return retval;
+}
+
+
/* See if the drive is ready and gather information about the tape. Return values:
< 0 negative error code from errno.h
0 drive ready
1 drive not ready (possibly no tape)
*/
-#define CHKRES_READY 0
-#define CHKRES_NOT_READY 1
static int check_tape(Scsi_Tape *STp, struct file *filp)
{
- int i, retval, new_session = FALSE;
+ int i, retval, new_session = FALSE, do_wait;
unsigned char cmd[MAX_COMMAND_SIZE], saved_cleaning;
unsigned short st_flags = filp->f_flags;
- Scsi_Request *SRpnt;
+ Scsi_Request *SRpnt = NULL;
ST_mode *STm;
ST_partstat *STps;
int dev = TAPE_NR(STp->devt);
@@ -668,32 +752,16 @@
}
STm = &(STp->modes[STp->current_mode]);
- memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
- cmd[0] = TEST_UNIT_READY;
-
saved_cleaning = STp->cleaning_req;
STp->cleaning_req = 0;
- SRpnt = st_do_scsi(NULL, STp, cmd, 0, SCSI_DATA_NONE, STp->long_timeout,
- MAX_READY_RETRIES, TRUE);
- if (!SRpnt) {
- retval = (STp->buffer)->syscall_result;
- goto err_out;
- }
- if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
- (SRpnt->sr_sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
+ do_wait = (blocking_open && (filp->f_flags & O_NONBLOCK) == 0);
+ retval = test_ready(STp, do_wait);
- /* Flush the queued UNIT ATTENTION sense data */
- for (i=0; i < 10; i++) {
- memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
- cmd[0] = TEST_UNIT_READY;
- SRpnt = st_do_scsi(SRpnt, STp, cmd, 0, SCSI_DATA_NONE,
- STp->long_timeout, MAX_READY_RETRIES, TRUE);
- if ((SRpnt->sr_sense_buffer[0] & 0x70) != 0x70 ||
- (SRpnt->sr_sense_buffer[2] & 0x0f) != UNIT_ATTENTION)
- break;
- }
+ if (retval < 0)
+ goto err_out;
+ if (retval == CHKRES_NEW_SESSION) {
(STp->device)->was_reset = 0;
STp->partition = STp->new_partition = 0;
if (STp->can_partitions)
@@ -710,26 +778,23 @@
}
new_session = TRUE;
}
- else
+ else {
STp->cleaning_req |= saved_cleaning;
- if ((STp->buffer)->syscall_result != 0) {
- if ((STp->device)->scsi_level >= SCSI_2 &&
- (SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
- (SRpnt->sr_sense_buffer[2] & 0x0f) == NOT_READY &&
- SRpnt->sr_sense_buffer[12] == 0x3a) { /* Check ASC */
- STp->ready = ST_NO_TAPE;
- } else
- STp->ready = ST_NOT_READY;
- scsi_release_request(SRpnt);
- SRpnt = NULL;
- STp->density = 0; /* Clear the erroneous "residue" */
- STp->write_prot = 0;
- STp->block_size = 0;
- STp->ps[0].drv_file = STp->ps[0].drv_block = (-1);
- STp->partition = STp->new_partition = 0;
- STp->door_locked = ST_UNLOCKED;
- return CHKRES_NOT_READY;
+ if (retval == CHKRES_NOT_READY || retval == CHKRES_NO_TAPE) {
+ if (retval == CHKRES_NO_TAPE)
+ STp->ready = ST_NO_TAPE;
+ else
+ STp->ready = ST_NOT_READY;
+
+ STp->density = 0; /* Clear the erroneous "residue" */
+ STp->write_prot = 0;
+ STp->block_size = 0;
+ STp->ps[0].drv_file = STp->ps[0].drv_block = (-1);
+ STp->partition = STp->new_partition = 0;
+ STp->door_locked = ST_UNLOCKED;
+ return CHKRES_NOT_READY;
+ }
}
if (STp->omit_blklims)
@@ -740,6 +805,10 @@
SRpnt = st_do_scsi(SRpnt, STp, cmd, 6, SCSI_DATA_READ, STp->timeout,
MAX_READY_RETRIES, TRUE);
+ if (!SRpnt) {
+ retval = (STp->buffer)->syscall_result;
+ goto err_out;
+ }
if (!SRpnt->sr_result && !SRpnt->sr_sense_buffer[0]) {
STp->max_block = ((STp->buffer)->b_data[1] << 16) |
@@ -763,6 +832,10 @@
SRpnt = st_do_scsi(SRpnt, STp, cmd, 12, SCSI_DATA_READ, STp->timeout,
MAX_READY_RETRIES, TRUE);
+ if (!SRpnt) {
+ retval = (STp->buffer)->syscall_result;
+ goto err_out;
+ }
if ((STp->buffer)->syscall_result != 0) {
DEBC(printk(ST_DEB_MSG "st%d: No Mode Sense.\n", dev));
@@ -915,11 +988,11 @@
/* Compute the usable buffer size for this SCSI adapter */
if (!(STp->buffer)->use_sg)
- (STp->buffer)->buffer_size = (STp->buffer)->sg[0].length;
+ (STp->buffer)->buffer_size = (STp->buffer)->sg_lengths[0];
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;
+ (STp->buffer)->buffer_size += (STp->buffer)->sg_lengths[i];
}
STp->write_prot = ((filp->f_flags & O_ACCMODE) == O_RDONLY);
@@ -935,6 +1008,12 @@
retval = check_tape(STp, filp);
if (retval < 0)
goto err_out;
+ if (blocking_open &&
+ (filp->f_flags & O_NONBLOCK) == 0 &&
+ retval != CHKRES_READY) {
+ retval = (-EIO);
+ goto err_out;
+ }
return 0;
err_out:
@@ -2229,14 +2308,16 @@
if (!retval) { /* SCSI command successful */
- if (!load_code)
+ if (!load_code) {
STp->rew_at_close = 0;
- else
+ STp->ready = ST_NO_TAPE;
+ }
+ else {
STp->rew_at_close = STp->autorew_dev;
-
- retval = check_tape(STp, filp);
- if (retval > 0)
- retval = 0;
+ retval = check_tape(STp, filp);
+ if (retval > 0)
+ retval = 0;
+ }
}
else {
STps = &(STp->ps[STp->partition]);
@@ -3330,9 +3411,12 @@
else
priority = GFP_KERNEL;
- i = sizeof(ST_buffer) + (st_max_sg_segs - 1) * sizeof(struct scatterlist);
+ i = sizeof(ST_buffer) + (st_max_sg_segs - 1) * sizeof(struct scatterlist) +
+ st_max_sg_segs * sizeof(unsigned int);
tb = kmalloc(i, priority);
if (tb) {
+ tb->sg_lengths = (unsigned int *)(&tb->sg[0] + st_max_sg_segs);
+
if (need_dma)
priority |= GFP_DMA;
@@ -3346,7 +3430,7 @@
tb->sg[0].address =
(unsigned char *) __get_free_pages(priority, order);
if (tb->sg[0].address != NULL) {
- tb->sg[0].length = b_size;
+ tb->sg_lengths[0] = b_size;
break;
}
}
@@ -3358,10 +3442,10 @@
for (b_size = PAGE_SIZE, order=0;
st_buffer_size >
- tb->sg[0].length + (ST_FIRST_SG - 1) * b_size;
+ tb->sg_lengths[0] + (ST_FIRST_SG - 1) * b_size;
order++, b_size *= 2)
;
- for (segs = 1, got = tb->sg[0].length;
+ for (segs = 1, got = tb->sg_lengths[0];
got < st_buffer_size && segs < ST_FIRST_SG;) {
tb->sg[segs].address =
(unsigned char *) __get_free_pages(priority,
@@ -3383,7 +3467,7 @@
break;
}
tb->sg[segs].page = NULL;
- tb->sg[segs].length = b_size;
+ tb->sg_lengths[segs] = b_size;
got += b_size;
segs++;
}
@@ -3403,7 +3487,7 @@
st_nbr_buffers, got, tb->sg_segs, need_dma, tb->b_data);
printk(ST_DEB_MSG
"st: segment sizes: first %d, last %d bytes.\n",
- tb->sg[0].length, tb->sg[segs - 1].length);
+ tb->sg_lengths[0], tb->sg_lengths[segs - 1]);
)
tb->in_use = in_use;
tb->dma = need_dma;
@@ -3457,7 +3541,7 @@
return FALSE;
}
STbuffer->sg[segs].page = NULL;
- STbuffer->sg[segs].length = b_size;
+ STbuffer->sg_lengths[segs] = b_size;
STbuffer->sg_segs += 1;
got += b_size;
STbuffer->buffer_size = got;
@@ -3477,11 +3561,11 @@
int i, order, b_size;
for (i = STbuffer->orig_sg_segs; i < STbuffer->sg_segs; i++) {
- for (b_size=PAGE_SIZE, order=0; b_size < STbuffer->sg[i].length;
+ for (b_size=PAGE_SIZE, order=0; b_size < STbuffer->sg_lengths[i];
order++, b_size *= 2)
; /* empty */
free_pages((unsigned long)(STbuffer->sg[i].address), order);
- STbuffer->buffer_size -= STbuffer->sg[i].length;
+ STbuffer->buffer_size -= STbuffer->sg_lengths[i];
}
DEB(
if (debugging && STbuffer->orig_sg_segs < STbuffer->sg_segs)
@@ -3500,15 +3584,15 @@
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;
+ i < st_bp->sg_segs && offset >= st_bp->sg_lengths[i]; i++)
+ offset -= st_bp->sg_lengths[i];
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;
+ cnt = st_bp->sg_lengths[i] - offset < do_count ?
+ st_bp->sg_lengths[i] - offset : do_count;
res = copy_from_user(st_bp->sg[i].address + offset, ubp, cnt);
if (res)
return (-EFAULT);
@@ -3533,15 +3617,15 @@
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;
+ i < st_bp->sg_segs && offset >= st_bp->sg_lengths[i]; i++)
+ offset -= st_bp->sg_lengths[i];
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;
+ cnt = st_bp->sg_lengths[i] - offset < do_count ?
+ st_bp->sg_lengths[i] - offset : do_count;
res = copy_to_user(ubp, st_bp->sg[i].address + offset, cnt);
if (res)
return (-EFAULT);
@@ -3560,6 +3644,25 @@
}
+/* Set the scatter/gather list length fields to sum up to the transfer length.
+ Return the number of segments being used. */
+static int set_sg_lengths(ST_buffer *st_bp, unsigned int length)
+{
+ int i;
+
+ for (i=0; i < st_bp->sg_segs; i++) {
+ if (length > st_bp->sg_lengths[i])
+ st_bp->sg[i].length = st_bp->sg_lengths[i];
+ else {
+ st_bp->sg[i].length = length;
+ break;
+ }
+ length -= st_bp->sg_lengths[i];
+ }
+ return i + 1;
+}
+
+
/* Validate the options from command line or module parameters */
static void validate_options(void)
{
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)