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

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)