patch-1.3.54 linux/drivers/block/ide-tape.c

Next file: linux/drivers/block/ide-tape.h
Previous file: linux/drivers/block/floppy.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.53/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
@@ -1,7 +1,7 @@
 /*
- * linux/drivers/block/ide-tape.c	Version 1.1 - ALPHA	Dec  14, 1995
+ * linux/drivers/block/ide-tape.c	Version 1.2 - ALPHA	Jan   1, 1996
  *
- * Copyright (C) 1995 Gadi Oxman <tgud@tochnapc2.technion.ac.il>
+ * Copyright (C) 1995, 1996 Gadi Oxman <tgud@tochnapc2.technion.ac.il>
  *
  * This driver was constructed as a student project in the software laboratory
  * of the faculty of electrical engineering in the Technion - Israel's
@@ -22,7 +22,7 @@
  * interface, on the other hand, creates new requests, adds them
  * to the request-list of the block device, and waits for their completion.
  *
- * Pipelined operation mode is now supported on writes.
+ * Pipelined operation mode is now supported on both reads and writes.
  *
  * The block device major and minor numbers are determined from the
  * tape's relative position in the ide interfaces, as explained in ide.c.
@@ -45,8 +45,18 @@
  * Our own ide-tape ioctl's can can be issued to either the block device or
  * the character device interface.
  *
- * Testing was done with a 2 GB CONNER CTMA 4000 IDE ATAPI Streaming Tape Drive.
+ * Maximal throughput with minimal bus load will usually be achieved in the
+ * following scenario:
+ *
+ *	1.	ide-tape is operating in the pipelined operation mode.
+ *	2.	All character device read/write requests consist of an
+ *		integral number of the tape's recommended data transfer unit
+ *		(which is shown on initialization and can be received with
+ *		 an ioctl).
+ *	3.	No buffering is performed by the user backup program.
  *
+ * Testing was done with a 2 GB CONNER CTMA 4000 IDE ATAPI Streaming Tape Drive.
+ * 
  * Ver 0.1   Nov  1 95   Pre-working code :-)
  * Ver 0.2   Nov 23 95   A short backup (few megabytes) and restore procedure
  *                        was successful ! (Using tar cvf ... on the block
@@ -135,6 +145,22 @@
  *                        downfalls as well, you may want to disable it.
  *                        A short explanation of the pipelined operation mode
  *                        is available below.
+ * Ver 1.2   Jan  1 96   Eliminated pipelined mode race condition.
+ *                       Added pipeline read mode. As a result, restores
+ *                        are now as fast as backups.
+ *                       Optimized shared interface behavior. The new behavior
+ *                        typically results in better IDE bus efficiency and
+ *                        higher tape throughput.
+ *                       Pre-calculation of the expected read/write request
+ *                        service time, based on the tape's parameters. In
+ *                        the pipelined operation mode, this allows us to
+ *                        adjust our polling frequency to a much lower value,
+ *                        and thus to dramatically reduce our load on Linux,
+ *                        without any decrease in performance.
+ *                       Implemented additional mtio.h operations.
+ *                       The recommended user block size is returned by
+ *                        the MTIOCGET ioctl.
+ *                       Additional minor changes.
  *
  * We are currently in an *alpha* stage. The driver is not complete and not
  * much tested. I would strongly suggest to:
@@ -153,34 +179,40 @@
  */
 
 /*
- * A short explanation of the pipelined operation mode.
+ * An overview of the pipelined operation mode.
  *
- * Pipelined mode is currently only implemented on writes. Reads are still
- * performed in the slow non-pipelined mode.
+ * In the pipelined write mode, we will usually just add requests to our
+ * pipeline and return immediately, before we even start to service them. The
+ * user program will then have enough time to prepare the next request while
+ * we are still busy servicing previous requests. In the pipelined read mode,
+ * the situation is similar - we add read-ahead requests into the pipeline,
+ * before the user even requested them.
+ *
+ * The pipeline can be viewed as a "safety net" which will be activated when
+ * the system load is high and prevents the user backup program from keeping up
+ * with the current tape speed. At this point, the pipeline will get
+ * shorter and shorter but the tape will still be streaming at the same speed.
+ * Assuming we have enough pipeline stages, the system load will hopefully
+ * decrease before the pipeline is completely empty, and the backup program
+ * will be able to "catch up" and refill the pipeline again.
+ * 
+ * When using the pipelined mode, it would be best to disable any type of
+ * buffering done by the user program, as ide-tape already provides all the
+ * benefits in the kernel, where it can be done in a more efficient way.
+ * As we will usually not block the user program on a request, the most
+ * efficient user code will then be a simple read-write-read-... cycle.
+ * Any additional logic will usually just slow down the backup process.
  *
- * The pipeline mode, when enough pipeline stages are available, manages to
- * keep the tape constantly streaming with the maximum device supported
- * throughput, regardless of the user backup program, since even when Linux
- * is busy doing other tasks, we still have job to be done.
- *
- * On my tape drive, using pipelined mode and giving the tape its own
- * interface and irq, I get a constant over 400 KBps throughput, which seems
- * to be the maximum throughput supported by my tape. When sharing the
- * interface between the tape and another ata-2 disk drive, I receive around
- * 350 KBps.
- *
- * Using the non-pipelined mode, I get anything between 150 to 380 KBps,
- * with the average being around 150 or 250 KBps, depending mainly on
- * the double buffering capabilities of the user backup program, but also
- * on some additional factors, such as the user block size and the ongoing
- * disk activity.
+ * Using the pipelined mode, I get a constant over 400 KBps throughput,
+ * which seems to be the maximum throughput supported by my tape.
  *
  * However, there are some downfalls:
  *
  *	1.	We use memory (for data buffers) in proportional to the number
  *		of pipeline stages (each stage is about 26 KB with my tape).
- *	2.	We cheat and postpone error codes to the user task. Again,
- *		the postponing period is proportional to the number of stages.
+ *	2.	In the pipelined write mode, we cheat and postpone error codes
+ *		to the user task. In read mode, the actual tape position
+ *		will be a bit further than the last requested block.
  *
  * Concerning (1):
  *
@@ -207,17 +239,46 @@
  *
  * Concerning (2):
  *
- *	In pipelined mode, ide-tape can not return accurate error codes to
- *      the user program since we usually just add the request to the
+ *	In pipelined write mode, ide-tape can not return accurate error codes
+ *	to the user program since we usually just add the request to the
  *      pipeline without waiting for it to be serviced. In case an error
  *      occurs, I will report it on the next user request.
  *
- *	For accurate error codes, you should disable pipelined mode.
+ *	In the pipelined read mode, subsequent read requests or forward
+ *	filemark spacing will perform correctly, as we preserve all blocks
+ *	and filemarks which we encountered during our excess read-ahead.
+ * 
+ *	For accurate tape positioning and error reporting, disabling
+ *	pipelined mode might be the best option.
  *
  * You can enable/disable/tune the pipelined operation mode by adjusting
  * the compile time parameters in ide-tape.h.
  */
- 
+
+/*
+ *	Possible improvements.
+ *
+ *	1.	Support for the ATAPI overlap protocol.
+ *
+ *		In order to maximize bus throughput, we currently use the DSC
+ *		overlap method which enables ide.c to service requests from the
+ *		other device while the tape is busy executing a command. The
+ *		DSC overlap method involves polling the tape's status register
+ *		for the DSC bit, and servicing the other device while the tape
+ *		isn't ready.
+ *
+ *		In the current QIC development standard (December 1995),
+ *		it is recommended that new tape drives will *in addition* 
+ *		implement the ATAPI overlap protocol, which is used for the
+ *		same purpose - efficient use of the IDE bus, but is interrupt
+ *		driven and thus has much less CPU overhead.
+ *
+ *		ATAPI overlap is likely to be supported in most new ATAPI
+ *		devices, including new ATAPI cdroms, and thus provides us
+ *		a method by which we can achieve higher throughput when
+ *		sharing a (fast) ATA-2 disk with any (slow) new ATAPI device.
+ */
+
 #include <linux/hdreg.h>
 #include <linux/types.h>
 #include <linux/string.h>
@@ -263,6 +324,7 @@
 #define	IDETAPE_INQUIRY_CMD		0x12
 #define	IDETAPE_ERASE_CMD		0x19
 #define	IDETAPE_MODE_SENSE_CMD		0x1a
+#define	IDETAPE_LOAD_UNLOAD_CMD		0x1b
 #define	IDETAPE_LOCATE_CMD		0x2b
 #define	IDETAPE_READ_POSITION_CMD	0x34
 
@@ -276,6 +338,14 @@
 #define	IDETAPE_SPACE_TO_EOD		3
 
 /*
+ *	Some defines for the LOAD UNLOAD command
+ */
+ 
+#define	IDETAPE_LU_LOAD_MASK		1
+#define	IDETAPE_LU_RETENSION_MASK	2
+#define	IDETAPE_LU_EOT_MASK		4
+
+/*
  *	Our ioctls - We will use 0x034n and 0x035n
  *
  *	Nothing special meanwhile.
@@ -345,6 +415,15 @@
 #define	IDETAPE_RQ_POSTPONED		0x1234
 
 /*
+ *	Error codes which are returned in rq->errors to the higher part
+ *	of the driver.
+ */
+
+#define	IDETAPE_RQ_ERROR_GENERAL	1 
+#define	IDETAPE_RQ_ERROR_FILEMARK	2
+#define	IDETAPE_RQ_ERROR_EOD		3
+
+/*
  *	ATAPI Task File Registers (Re-definition of the ATA Task File
  *	Registers for an ATAPI packet command).
  * 	From Table 3-2 of QIC-157C.
@@ -709,7 +788,6 @@
 void idetape_output_data (ide_drive_t *drive,void *buffer, unsigned long bcount);
 void idetape_discard_data (ide_drive_t *drive, unsigned long bcount);
 
-
 /*
  *	Packet command related functions.
  */
@@ -724,6 +802,7 @@
 void idetape_postpone_request (ide_drive_t *drive);
 void idetape_poll_for_dsc (unsigned long data);
 void idetape_poll_for_dsc_direct (unsigned long data);
+void idetape_put_back_postponed_request (ide_drive_t *drive);
 void idetape_media_access_finished (ide_drive_t *drive);
 
 /*
@@ -734,10 +813,16 @@
 void idetape_retry_pc (ide_drive_t *drive);
 void idetape_zero_packet_command (idetape_packet_command_t *pc);
 void idetape_queue_pc_head (ide_drive_t *drive,idetape_packet_command_t *pc,struct request *rq);
+void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result);
 
 idetape_packet_command_t *idetape_next_pc_storage (ide_drive_t *drive);
 struct request *idetape_next_rq_storage (ide_drive_t *drive);
 
+/*
+ *	idetape_end_request is used to finish servicing a request, and to
+ *	insert a pending pipeline request into the main device queue.
+ */
+ 
 void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
 
 /*
@@ -749,6 +834,7 @@
 void idetape_create_locate_cmd (idetape_packet_command_t *pc,unsigned long block,byte partition);
 void idetape_create_rewind_cmd (idetape_packet_command_t *pc);
 void idetape_create_write_filemark_cmd (idetape_packet_command_t *pc,int write_filemark);
+void idetape_create_load_unload_cmd (idetape_packet_command_t *pc,int cmd);
 void idetape_create_space_cmd (idetape_packet_command_t *pc,long count,byte cmd);
 void idetape_create_erase_cmd (idetape_packet_command_t *pc);
 void idetape_create_test_unit_ready_cmd (idetape_packet_command_t *pc);
@@ -763,7 +849,6 @@
 void idetape_request_sense_callback (ide_drive_t *drive);
 
 void idetape_display_inquiry_result (byte *buffer);
-void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result);
 
 /*
  *	Character device callback functions.
@@ -791,11 +876,26 @@
 int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count);
 
 /*
+ *	idetape_space_over_filemarks handles the MTFSF, MTFSFM, ... mtio.h
+ *	commands.
+ */
+ 
+int idetape_space_over_filemarks (ide_drive_t *drive,short mt_op,int mt_count);
+
+/*
+ *	idetape_add_chrdev_read_request is called from idetape_chrdev_read
+ *	to service a character device read request and add read-ahead
+ *	requests to our pipeline.
+ */
+ 
+int idetape_add_chrdev_read_request (ide_drive_t *drive,int blocks,char *buffer);
+
+/*
  *	idetape_add_chrdev_write_request adds a character device write
  *	request to the pipeline.
  */
  
-int idetape_add_chrdev_write_request (ide_drive_t *drive,int cmd,int blocks,char *buffer);
+int idetape_add_chrdev_write_request (ide_drive_t *drive,int blocks,char *buffer);
 
 /*
  *	idetape_queue_rw_tail will add a command to the tail of the device
@@ -814,6 +914,7 @@
 
 int idetape_position_tape (ide_drive_t *drive,unsigned long block);
 int idetape_rewind_tape (ide_drive_t *drive);
+int idetape_flush_tape_buffers (ide_drive_t *drive);
 
 /*
  *	Used to get device information
@@ -838,8 +939,10 @@
 void idetape_copy_buffer_to_stage (idetape_pipeline_stage_t *stage,char *buffer);
 void idetape_increase_max_pipeline_stages (ide_drive_t *drive);
 void idetape_add_stage_tail (ide_drive_t *drive,idetape_pipeline_stage_t *stage);
+void idetape_remove_stage_head (ide_drive_t *drive);
 void idetape_active_next_stage (ide_drive_t *drive);
-void idetape_empty_pipeline (ide_drive_t *drive);
+void idetape_empty_read_pipeline (ide_drive_t *drive);
+void idetape_empty_write_pipeline (ide_drive_t *drive);
 void idetape_insert_pipeline_into_queue (ide_drive_t *drive);
 
 /*
@@ -901,14 +1004,14 @@
 	struct idetape_id_gcw gcw;
 	unsigned short *ptr;
 	int support=1;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	unsigned short mask,i;
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		
 	ptr=(unsigned short *) &gcw;
 	*ptr=id->config;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Dumping ATAPI Identify Device tape parameters\n");
 	
 	printk ("Protocol Type: ");
@@ -1004,7 +1107,7 @@
 	else {
 		printk ("According to the device, fields 64-70 are not valid.\n");
 	}
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	/* Check that we can support this device */
 
@@ -1100,10 +1203,11 @@
 {
 	idetape_tape_t *tape=&(drive->tape);
 	unsigned int allocation_length;
-	
-#if IDETAPE_DEBUG
+	double service_time,nr_units;
+		
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Reached idetape_setup\n");
-#endif /* IDETAPE_DEBUG */	
+#endif /* IDETAPE_DEBUG_LOG */	
 	
 	drive->ready_stat = 0;			/* With an ATAPI device, we can issue packet commands */
 						/* regardless of the state of DRDY */
@@ -1117,22 +1221,18 @@
 	tape->busy=0;
 	tape->active_data_request=NULL;
 	tape->current_number_of_stages=0;
-	tape->first_stage=tape->last_stage=NULL;
-	tape->pipeline_was_full_once=0;
+	tape->first_stage=tape->next_stage=tape->last_stage=NULL;
 	tape->error_in_pipeline_stage=0;
-	tape->pipeline_locked=0;
-
 	tape->request_status=0;
-	tape->request_dsc_callback=0;
-	
+	tape->chrdev_direction=idetape_direction_none;
+
 #if IDETAPE_PIPELINE
 	tape->max_number_of_stages=IDETAPE_MIN_PIPELINE_STAGES;
-	printk ("ide-tape: Operating in pipelined (fast and tricky) write mode.\n");
+	printk ("ide-tape: Operating in pipelined (fast and tricky) operation mode.\n");
 #else
 	tape->max_number_of_stages=0;
-	printk ("ide-tape: Operating in non-pipelined (slow and safe) write mode.\n");
+	printk ("ide-tape: Operating in non-pipelined (slow and safe) operation mode.\n");
 #endif /* IDETAPE_PIPELINE */
-	printk ("ide-tape: Operating in non-pipelined (slow and safe) read mode.\n");
 
 	idetape_get_mode_sense_results (drive);
 
@@ -1152,6 +1252,58 @@
 		return;
 	}
 
+#if IDETAPE_ANTICIPATE_READ_WRITE_DSC
+
+	/*
+	 *	Cleverly select the DSC read/write polling frequency, based
+	 *	on the tape's speed, its recommended transfer unit, its
+	 *	internal buffer size and our operation mode.
+	 *
+	 *	In the pipelined operation mode we aim for "catching" the
+	 *	tape when its internal buffer is about 50% full. This will
+	 *	dramatically reduce our polling frequency and will also
+	 *	leave enough time for the ongoing request of the other device
+	 *	to complete before the buffer is completly empty. We will
+	 *	then completly refill the buffer with requests from our
+	 *	internal pipeline.
+	 *
+	 *	When operating in the non-pipelined operation mode, we
+	 *	can't allow ourself this luxury. Instead, we will try to take
+	 *	full advantage of the internal tape buffer by waiting only
+	 *	for one request to complete. This will increase our load
+	 *	on linux but will usually still fail to keep the tape
+	 *	constantly streaming.
+	 */
+
+	service_time=((double) tape->data_buffer_size/1024.0)/((double) tape->capabilities.speed*(1000.0/1024.0));
+	nr_units=(double) tape->capabilities.buffer_size*512.0/(double) tape->data_buffer_size;
+
+	if (tape->max_number_of_stages)	
+		tape->best_dsc_rw_frequency=(unsigned long) (0.5*nr_units*service_time*HZ);
+	else		
+		tape->best_dsc_rw_frequency=(unsigned long) (service_time*HZ);
+	
+	/*
+	 *	Ensure that the number we got makes sense.
+	 */
+
+	if (tape->best_dsc_rw_frequency > IDETAPE_DSC_READ_WRITE_LOWEST_FREQUENCY) {
+		printk ("ide-tape: Although the recommended polling period is %lu jiffies, \n",tape->best_dsc_rw_frequency);
+		printk ("ide-tape: we will use %u jiffies\n",IDETAPE_DSC_READ_WRITE_LOWEST_FREQUENCY);
+		printk ("ide-tape: (It may well be that we are wrong here)\n");
+		tape->best_dsc_rw_frequency = IDETAPE_DSC_READ_WRITE_LOWEST_FREQUENCY;
+	}
+
+	if (tape->best_dsc_rw_frequency < IDETAPE_DSC_READ_WRITE_FALLBACK_FREQUENCY) {
+		printk ("ide-tape: Although the recommended polling period is %lu jiffies, \n",tape->best_dsc_rw_frequency);
+		printk ("ide-tape: we will use %u jiffies\n",IDETAPE_DSC_READ_WRITE_FALLBACK_FREQUENCY);
+		tape->best_dsc_rw_frequency = IDETAPE_DSC_READ_WRITE_FALLBACK_FREQUENCY;
+	}
+
+#else
+	tape->best_dsc_rw_frequency=IDETAPE_DSC_READ_WRITE_FALLBACK_FREQUENCY;
+#endif /* IDETAPE_ANTICIPATE_READ_WRITE_DSC */
+
 	printk ("ide-tape: Tape speed - %d KBps. Recommended transfer unit - %d bytes.\n",tape->capabilities.speed,tape->data_buffer_size);
 
 	return;
@@ -1193,11 +1345,13 @@
 		printk ("ide-tape: Can't get tape parameters\n");
 		printk ("ide-tape: Assuming some default parameters\n");
 		tape->tape_block_size=512;
-		tape->capabilities.ctl=26*1024;
+		tape->capabilities.ctl=52;
+		tape->capabilities.speed=450;
+		tape->capabilities.buffer_size=6*52;
 		return;
 	}
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Dumping the results of the MODE SENSE packet command\n");
 	printk ("Mode Parameter Header:\n");
 	printk ("Mode Data Length - %d\n",header->mode_data_length);
@@ -1225,7 +1379,7 @@
 	printk ("Continuous transfer limits in blocks - %d\n",capabilities->ctl);
 	printk ("Current speed in KBps - %d\n",capabilities->speed);	
 	printk ("Buffer size - %d\n",capabilities->buffer_size*512);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 }
 
 /*
@@ -1280,36 +1434,43 @@
 
 	tape=&(drive->tape);
 	        
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_BUGS
 	if (tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
 		printk ("ide-tape: ide-tape.c bug - Two request sense in serial were issued\n");
-		/* ??? Need to rethink about that */		
 	}
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 
 	if (tape->failed_pc == NULL && pc->c[0] != IDETAPE_REQUEST_SENSE_CMD)
 		tape->failed_pc=pc;
 	tape->pc=pc;							/* Set the current packet command */
 
-	if (pc->retries > IDETAPE_MAX_PC_RETRIES) {
-		printk ("ide-tape: %s: I/O error, ",drive->name);
-		printk ("pc = %x, key = %x, asc = %x, ascq = %x\n",pc->c[0],tape->sense_key,tape->asc,tape->ascq);
-		printk ("ide-tape: Maximum retries reached - Giving up\n");
-		pc->error=1;					/* Giving up */
-		pc->active=0;
+	if (pc->retries > IDETAPE_MAX_PC_RETRIES || pc->abort) {
+
+		/*
+		 *	We will "abort" retrying a packet command in case
+		 *	a legitimate error code was received (crossing a
+		 *	filemark, for example). We will not log those errors.
+		 */
+
+		if (!pc->abort) {
+			printk ("ide-tape: %s: I/O error, ",drive->name);
+			printk ("pc = %x, key = %x, asc = %x, ascq = %x\n",pc->c[0],tape->sense_key,tape->asc,tape->ascq);
+			printk ("ide-tape: Maximum retries reached - Giving up\n");
+			pc->error=1;					/* Giving up */
+		}
 		tape->failed_pc=NULL;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_BUGS
 		if (pc->callback==NULL)
 			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
 		else
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 			(*pc->callback)(drive);
 		return;
 	}
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Retry number - %d\n",pc->retries);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	pc->retries++;
 
@@ -1319,7 +1480,6 @@
  */
  
 	pc->actually_transferred=0;					/* We haven't transferred any data yet */
-	pc->active=1;							/* Packet command started */
 	bcount.all=pc->request_transfer;				/* Request to transfer the entire buffer at once */
 
 									/* Initialize the task file registers */
@@ -1366,37 +1526,35 @@
 void idetape_pc_intr (ide_drive_t *drive)
 
 {
-	idetape_tape_t *tape;
+	idetape_tape_t *tape=&(drive->tape);
 	idetape_status_reg_t status;
 	idetape_bcount_reg_t bcount;
         idetape_ireason_reg_t ireason;
-	idetape_packet_command_t *pc;
-	
+	idetape_packet_command_t *pc=tape->pc;
 	unsigned long temp;
 
-	tape=&(drive->tape);
-	
 	status.all=IN_BYTE (IDETAPE_STATUS_REG);		/* Clear the interrupt */
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Reached idetape_pc_intr interrupt handler\n");
-#endif /* IDETAPE_DEBUG */	
+#endif /* IDETAPE_DEBUG_LOG */	
 
-	pc=tape->pc;						/* Current packet command */
-		
 	if (!status.b.drq) {					/* No more interrupts */
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("Packet command completed\n");
 		printk ("Total bytes transferred: %lu\n",pc->actually_transferred);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
+		
+		sti ();
+
 		if (status.b.check) {					/* Error detected */
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	/*
 	 *	Without debugging, we only log an error if we decided to
 	 *	give up retrying.
 	 */
 			printk ("ide-tape: %s: I/O error, ",drive->name);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 			idetape_retry_pc (drive);			/* Retry operation */
 			return;
 		}
@@ -1406,14 +1564,13 @@
 			idetape_postpone_request (drive);		/* Allow ide.c to handle other requests */
 			return;
 		}
-		pc->active=0;
 		if (tape->failed_pc == pc)
 			tape->failed_pc=NULL;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_BUGS
 		if (pc->callback==NULL)			
 			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
 		else
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 			(*pc->callback)(drive);			/* Command finished - Call the callback function */
 		return;
 	}
@@ -1446,7 +1603,7 @@
 				printk ("Allowing transfer\n");
 		}
 	}
-#if IDETAPE_DEBUG	
+#if IDETAPE_DEBUG_BUGS	
 	if (bcount.all && !pc->buffer) {	
 		printk ("ide-tape: ide-tape.c bug - Buffer not set in idetape_pc_intr. Discarding data.\n");
 		
@@ -1459,14 +1616,14 @@
 		else {	/* ??? */
 		}
 	}
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 	if (pc->writing)
 		idetape_output_data (drive,pc->current_position,bcount.all);	/* Write the current buffer */
 	else
 		idetape_input_data (drive,pc->current_position,bcount.all);	/* Read the current buffer */
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: %s %d bytes\n",pc->writing ? "Wrote":"Received",bcount.all);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	pc->actually_transferred+=bcount.all;					/* Update the current position */
 	pc->current_position+=bcount.all;
 
@@ -1482,21 +1639,17 @@
 void idetape_postpone_request (ide_drive_t *drive)
 
 {
-	idetape_tape_t *tape;
-	unsigned long flags;
+	idetape_tape_t *tape=&(drive->tape);
 	struct request *rq;
 	idetape_status_reg_t status;
 	
-	tape=&(drive->tape);
-	status.all=IN_BYTE (IDETAPE_STATUS_REG);
-
-	sti ();
-	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_postpone_request\n");
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
 	if (tape->postponed_rq != NULL)
 		printk ("ide-tape.c bug - postponed_rq not NULL in idetape_postpone_request\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 
 	tape->dsc_timer.expires=jiffies + tape->dsc_polling_frequency;	/* Set timer to poll for */
 	tape->dsc_timeout=jiffies+IDETAPE_DSC_TIMEOUT;			/* actual completion */
@@ -1510,14 +1663,16 @@
 
 	tape->postponed_rq = rq = HWGROUP(drive)->rq;
 	rq->rq_status = IDETAPE_RQ_POSTPONED;	
-	save_flags(flags);cli ();					/* Let ide.c handle another request */
 	blk_dev[MAJOR(rq->rq_dev)].current_request = rq->next;
 	HWGROUP(drive)->rq = NULL;
-	restore_flags(flags);
-	
-	tape->request_status=0;
-	tape->request_dsc_callback=0;
+
+	/*
+	 *	Check the status again - Maybe we can save one polling period.
+	 */
+	 
+	status.all=IN_BYTE (IDETAPE_STATUS_REG);
 	tape->last_status=status.all;
+	tape->request_status=1;	
 	
 	tape->dsc_polling_start=jiffies;
 	add_timer(&(tape->dsc_timer));		/* Activate the polling timer */
@@ -1533,22 +1688,16 @@
 
 {
 	ide_drive_t *drive=(ide_drive_t *) data;
-	ide_hwgroup_t *hwgroup=HWGROUP (drive);
 	idetape_tape_t *tape=&(drive->tape);
 	idetape_status_reg_t status;
-	unsigned long flags;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("%s: idetape_poll_for_dsc_direct called\n",drive->name);
-#endif /* IDETAPE_DEBUG */	
+#endif /* IDETAPE_DEBUG_LOG */	
 
-	save_flags (flags);cli ();
 	OUT_BYTE(drive->select.all,IDE_SELECT_REG);
 	status.all=IN_BYTE (IDETAPE_STATUS_REG);
-	if (hwgroup->drive != NULL)
-		OUT_BYTE (hwgroup->drive->select.all,IDE_SELECT_REG);
-	restore_flags (flags);
-
+	
 	if (status.b.dsc) {					/* DSC received */
 		tape->dsc_received=1;
 		del_timer (&(tape->dsc_timer));			/* Stop polling and put back the postponed */
@@ -1579,17 +1728,15 @@
  *	by idetape_postpone_request) to poll for the DSC bit
  *	in the status register.
  *
- *	We take care not to perform any tape access and not to touch the
- *	device request queue if the driver is accessing the other device.
- *	We will instead ask ide.c to perform those functions on the next
- *	call to do_request, at the point in which the other device is idle.
- *
- *	However, in case the other device is already idle, we will read
- *	the status register from our timer handler.
- *
- *	I am also a bit paranoid with the use of cli (), all through the
- *	code. I still need to think harder about each one whether we can
- *	avoid it and still be free of race conditions ...
+ *	We take care not to perform any tape access if the driver is
+ *	accessing the other device. We will instead ask ide.c to sample
+ *	the tape status register on our behalf in the next call to do_request,
+ *	at the point in which the other device is idle, or assume that
+ *	DSC was received even though we won't verify it (but when we assume
+ *	that, it will usually have a solid basis).
+ *
+ *	The use of cli () below is a must, as we inspect and change
+ *	the device request list while another request is active.
  */
  
 void idetape_poll_for_dsc (unsigned long data)
@@ -1600,14 +1747,13 @@
 	idetape_tape_t *tape=&(drive->tape);
 	struct blk_dev_struct *bdev = &blk_dev[major];
 	unsigned long flags;
-	
 	idetape_status_reg_t status;
 
-	save_flags (flags);cli ();
-			
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("%s: idetape_poll_for_dsc called\n",drive->name);
-#endif /* IDETAPE_DEBUG */	
+#endif /* IDETAPE_DEBUG_LOG */	
+
+	save_flags (flags);cli ();
 
 	/*
 	 *	Check if the other device is idle. If there are no requests,
@@ -1615,63 +1761,159 @@
 	 */
 
 	if (bdev->current_request == NULL) {
-		restore_flags (flags);
+		sti ();
 		idetape_poll_for_dsc_direct (data);
 		return;
 	}
-	
-	if (bdev->current_request->next == NULL) {
-		/*
-		 *	There will not be another request after the currently
-		 *	ongoing request, so ide.c won't be able to sample
-		 *	the status register on our behalf in do_request. Just
-		 *	give up and poll again (in a faster frequency), until
-		 *	we are lucky.
-		 */
+
+	/*
+	 *	If DSC was received, re-insert our postponed request into
+	 *	the request queue (using ide_next).
+	 */
+
+	status.all=tape->last_status;
+
+	if (status.b.dsc) {					/* DSC received */
+		tape->dsc_received=1;
+		idetape_put_back_postponed_request (drive);
+		del_timer (&(tape->dsc_timer));
 		restore_flags (flags);
-		tape->dsc_timer.expires = jiffies + tape->dsc_polling_frequency/2;
-		add_timer(&(tape->dsc_timer));
 		return;
 	}
-	
+
 	/*
-	 *	We now know that:
+	 *	At this point, DSC may have been received, but we can't
+	 *	check it. We now have two options:
+	 *
+	 *		1.	The "simple" method - We can continue polling
+	 *			until we know the value of DSC.
+	 *
+	 *	but we also have a more clever option :-)
+	 *
+	 *		2.	We can sometimes more or less anticipate in
+	 *			advance how much time it will take for
+	 *			the tape to perform the request. This is the
+	 *			place to take advantage of this !
 	 *
-	 *		1.	The ide driver is potentially accessing
-	 *			the other device -- We can not touch it.
-	 *		2.	do_request will be called after the current
-	 *			request is finished.
+	 *			We can assume that DSC was received, put
+	 *			back our request, and hope that we will have
+	 *			a "cache hit". This will only work when
+	 *			we haven't initiated the packet command yet,
+	 *			but this is the common read/write case. As
+	 *			for the slower media access commands, fallback
+	 *			to method 1 above.
 	 *
-	 *	We will therefor ask ide.c to perform the tasks on our behalf.
+	 *	When using method 2, we can also take advantage of the
+	 *	knowledge of the tape's internal buffer size - We can
+	 *	precalculate the time it will take for the tape to complete
+	 *	servicing not only one request, but rather, say, 50% of its
+	 *	internal buffer. The polling period will then be much larger,
+	 *	decreasing our load on Linux, and we will also call
+	 *	idetape_postpone_request less often, as there will usually
+	 *	be more room in the internal tape buffer while we are in
+	 *	idetape_do_request.
+	 *
+	 *	For this method to work well, the ongoing request of the
+	 *	other device should be serviced by the time the tape is
+	 *	still working on its remaining 50% internal buffer. This
+	 *	will usually happen when the other device is much faster
+	 *	than the tape.
+	 */
+
+#if IDETAPE_ANTICIPATE_READ_WRITE_DSC
+
+	/*
+	 *	Method 2.
+	 *
+	 *	There is a high chance that DSC was received, even though
+	 *	we couldn't verify it. Let's hope that it's a "cache hit"
+	 *	rather than a "cache miss". Someday I will probably add a
+	 *	feedback loop around the number of "cache hits" which will
+	 *	fine-tune the polling period.
 	 */
 	 
-	status.all=tape->last_status;
-	
-	if (status.b.dsc) {					/* DSC received */
-		tape->dsc_received=1;
-		del_timer (&(tape->dsc_timer));			/* Stop polling and request ide.c to call */
-		tape->request_dsc_callback=1;			/* our idetape_put_back_postponed_request later */
-		restore_flags (flags);		
+	if (tape->postponed_rq->cmd != IDETAPE_PACKET_COMMAND_REQUEST_TYPE1) {
+
+		/*
+		 *	We can use this method only when the packet command
+		 *	was still not initiated.
+		 */
+		 
+		idetape_put_back_postponed_request (drive);
+		del_timer (&(tape->dsc_timer));
+		restore_flags (flags);
 		return;
 	}
+#endif /* IDETAPE_ANTICIPATE_READ_WRITE_DSC */
 
-	if (jiffies > tape->dsc_timeout) 	{ 		/* Timeout */
-		tape->dsc_received=1;
+	/*
+	 *	Fallback to method 1.
+	 */
+	 	
+	if (bdev->current_request->next == NULL) {
+		/*
+		 *	There will not be another request after the currently
+		 *	ongoing request, so ide.c won't be able to sample
+		 *	the status register on our behalf in do_request.
+		 *
+		 *	In case we are waiting for DSC before the packet
+		 *	command was initiated, we will put back our postponed
+		 *	request and have another look at the status register
+		 *	in idetape_do_request, as done in method 2 above.
+		 *
+		 *	In case we already initiated the command, we can't
+		 *	put it back, but it is anyway a slow media access
+		 *	command. We will just give up and poll again until
+		 *	we are lucky.
+		 */
+
+		if (tape->postponed_rq->cmd == IDETAPE_PACKET_COMMAND_REQUEST_TYPE1) {
+
+			/*
+			 *	Media access command - Poll again.
+			 *
+			 *	We set tape->request_status to 1, just in case
+			 *	other requests are added while we are waiting.
+			 */
+			 
+			tape->request_status=1;
+			restore_flags (flags);
+			tape->dsc_timer.expires = jiffies + tape->dsc_polling_frequency;
+			add_timer(&(tape->dsc_timer));
+			return;
+		}
+		
+		/*
+		 *	The packet command hasn't been sent to the tape yet -
+		 *	We can safely put back the request and have another
+		 *	look at the status register in idetape_do_request.
+		 */
+
+		idetape_put_back_postponed_request (drive);
 		del_timer (&(tape->dsc_timer));
-		/* ??? */
-		tape->request_dsc_callback=1;
 		restore_flags (flags);
 		return;
 	}
 
 	/*
-	 *	Request ide.c to sample for us the tape's status register on
-	 *	the next time in which it can be safely done.
+	 *	There will be another request after the current request.
+	 *
+	 *	Request ide.c to sample for us the tape's status register
+	 *	before the next request.
 	 */
 
 	tape->request_status=1;
 	restore_flags (flags);
-		 
+
+	if (jiffies > tape->dsc_timeout) 	{ 		/* Timeout */
+		tape->dsc_received=0;
+		/* ??? */
+		idetape_put_back_postponed_request (drive);
+		del_timer (&(tape->dsc_timer));
+		restore_flags (flags);
+		return;
+	}
+
 	/* Poll again */
 	
 	if (jiffies - tape->dsc_polling_start > IDETAPE_FAST_SLOW_THRESHOLD)
@@ -1683,9 +1925,8 @@
 }
 
 /*
- *	idetape_put_back_postponed_request gets called by do_request
- *	in ide.c when we decided to stop polling for DSC and continue
- *	servicing our postponed request.
+ *	idetape_put_back_postponed_request gets called when we decided to
+ *	stop polling for DSC and continue servicing our postponed request.
  */
 
 void idetape_put_back_postponed_request (ide_drive_t *drive)
@@ -1693,10 +1934,15 @@
 {
 	idetape_tape_t *tape = &(drive->tape);
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Putting back postponed request\n");
-#endif /* IDETAPE_DEBUG */
-
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+	if (tape->postponed_rq == NULL) {
+		printk ("tape->postponed_rq is NULL in put_back_postponed_request\n");
+		return;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
 	(void) ide_do_drive_cmd (drive, tape->postponed_rq, ide_next);
 
 	/*
@@ -1723,10 +1969,9 @@
 	status.all=IN_BYTE (IDETAPE_STATUS_REG);
 
 	if (tape->dsc_received) {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("DSC received\n");
-#endif /* IDETAPE_DEBUG */
-		pc->active=0;
+#endif /* IDETAPE_DEBUG_LOG */
 		if (status.b.check) {					/* Error detected */
 			printk ("ide-tape: %s: I/O error, ",drive->name);
 			idetape_retry_pc (drive);			/* Retry operation */
@@ -1735,26 +1980,25 @@
 		pc->error=0;
 		if (tape->failed_pc == pc)
 			tape->failed_pc=NULL;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_BUGS
 		if (pc->callback==NULL)
 			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
 		else
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 			(*pc->callback)(drive);
 
 		return;
 	}
 	else {
-		pc->active=0;
 		printk ("ide-tape: %s: DSC timeout.\n",drive->name);
 		/* ??? */
 		pc->error=1;
 		tape->failed_pc=NULL;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_BUGS
 		if (pc->callback==NULL)
 			printk ("ide-tape: ide-tape bug - Callback function not set !\n");
 		else
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 			(*pc->callback)(drive);
 		return;
 	}
@@ -1797,17 +2041,16 @@
 	tape=&(drive->tape);
 	rq=HWGROUP(drive)->rq;
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Reached idetape_pc_callback\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	if (!tape->pc->error) {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("Request completed\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		idetape_end_request (1,HWGROUP (drive));
 	}
 	else {
-		printk ("Aborting request\n");
 		idetape_end_request (0,HWGROUP (drive));
 	}
 	return;
@@ -1822,21 +2065,26 @@
 
 	tape=&(drive->tape);	
 	rq=HWGROUP(drive)->rq;
-#if IDETAPE_DEBUG	
+#if IDETAPE_DEBUG_LOG	
 	printk ("ide-tape: Reached idetape_read_callback\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	tape->block_address+=tape->pc->actually_transferred/tape->tape_block_size;
 	if (!tape->pc->error) {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("Request completed\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		rq->sector+=rq->current_nr_sectors;
 		rq->nr_sectors-=rq->current_nr_sectors;
 		rq->current_nr_sectors=0;
 		idetape_end_request (1,HWGROUP (drive));
 	}
 	else {
-		printk ("Aborting request\n");
+		rq->errors=tape->pc->error;
+		switch (rq->errors) {
+			case IDETAPE_RQ_ERROR_FILEMARK:
+			case IDETAPE_RQ_ERROR_EOD:
+				break;
+		}
 		idetape_end_request (0,HWGROUP (drive));
 	}
 	return;
@@ -1850,21 +2098,20 @@
 	
 	tape=&(drive->tape);
 	rq=HWGROUP(drive)->rq;
-#if IDETAPE_DEBUG	
+#if IDETAPE_DEBUG_LOG	
 	printk ("ide-tape: Reached idetape_write_callback\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	tape->block_address+=tape->pc->actually_transferred/tape->tape_block_size;
 	if (!tape->pc->error) {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("Request completed\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		rq->sector+=rq->current_nr_sectors;
 		rq->nr_sectors-=rq->current_nr_sectors;
 		rq->current_nr_sectors=0;
 		idetape_end_request (1,HWGROUP (drive));
 	}
 	else {
-		printk ("Aborting request\n");
 		idetape_end_request (0,HWGROUP (drive));
 	}
 	return;
@@ -1948,9 +2195,9 @@
 void idetape_create_inquiry_cmd (idetape_packet_command_t *pc)
 
 {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating INQUIRY packet command\n");
-#endif /* IDETAPE_DEBUG */	
+#endif /* IDETAPE_DEBUG_LOG */	
 	pc->request_transfer=36;
 	pc->callback=&idetape_inquiry_callback;
 	pc->writing=0;
@@ -1979,7 +2226,7 @@
 		printk ("Assuming QIC-157C format.\n");
 	}
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Dumping INQUIRY command results:\n");
 	printk ("Response Data Format: %d - ",result->response_format);
 	switch (result->response_format) {
@@ -2031,7 +2278,7 @@
 	printk ("Vendor Identification: %s\n",result->vendor_id);
 	printk ("Product Identification: %s\n",result->product_id);
 	printk ("Product Revision Level: %s\n",result->revision_level);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	if (result->device_type != 1)
 		printk ("Device type is not set to tape\n");
@@ -2048,9 +2295,9 @@
 void idetape_create_request_sense_cmd (idetape_packet_command_t *pc)
 
 {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating REQUEST SENSE packet command\n");
-#endif /* IDETAPE_DEBUG */	
+#endif /* IDETAPE_DEBUG_LOG */	
 	pc->request_transfer=18;
 	pc->callback=&idetape_request_sense_callback;
 	pc->writing=0;
@@ -2063,19 +2310,15 @@
 void idetape_request_sense_callback (ide_drive_t *drive)
 
 {
-	idetape_tape_t *tape;
-	struct request *rq;
+	idetape_tape_t *tape=&(drive->tape);
 
-	tape=&(drive->tape);	
-	rq=HWGROUP(drive)->rq;
-	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Reached idetape_request_sense_callback\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	if (!tape->pc->error) {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("Request completed\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		idetape_analyze_error (drive,(idetape_request_sense_result_t *) tape->pc->buffer);
 		idetape_end_request (1,HWGROUP (drive));
 	}
@@ -2095,28 +2338,41 @@
 void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result)
 
 {
-	idetape_tape_t *tape;
-	
-	tape=&(drive->tape);
+	idetape_tape_t *tape=&(drive->tape);
+	idetape_packet_command_t *pc=tape->failed_pc;
+		
 	tape->sense_key=result->sense_key;
 	tape->asc=result->asc;
 	tape->ascq=result->ascq;
-#if IDETAPE_DEBUG	
+	
+#if IDETAPE_DEBUG_LOG	
 	/*
 	 *	Without debugging, we only log an error if we decided to
 	 *	give up retrying.
 	 */
-	printk ("ide-tape: sense key = %x, asc = %x, ascq = %x\n",result->sense_key,result->asc,result->ascq);
-#endif /* IDETAPE_DEBUG */	
-	return;	
+	printk ("ide-tape: pc = %x, sense key = %x, asc = %x, ascq = %x\n",pc->c[0],result->sense_key,result->asc,result->ascq);
+#endif /* IDETAPE_DEBUG_LOG */
+
+	if (pc->c[0] == IDETAPE_READ_CMD) {
+		if (result->filemark) {
+			pc->error=IDETAPE_RQ_ERROR_FILEMARK;
+			pc->abort=1;
+			return;
+		}
+		if (result->sense_key == 8) {
+			pc->error=IDETAPE_RQ_ERROR_EOD;
+			pc->abort=1;
+			return;
+		}
+	}
 }
 
 void idetape_create_test_unit_ready_cmd (idetape_packet_command_t *pc)
 
 {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating TEST UNIT READY packet command\n");
-#endif /* IDETAPE_DEBUG */	
+#endif /* IDETAPE_DEBUG_LOG */	
 	pc->request_transfer=0;
 	pc->buffer=NULL;
 	pc->current_position=NULL;
@@ -2132,9 +2388,9 @@
 {
 	unsigned long *ptr;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating LOCATE packet command\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	pc->request_transfer=0;
 	pc->buffer=NULL;
 	pc->current_position=NULL;
@@ -2154,9 +2410,9 @@
 void idetape_create_rewind_cmd (idetape_packet_command_t *pc)
 
 {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating REWIND packet command\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	pc->request_transfer=0;
 	pc->buffer=NULL;
 	pc->current_position=NULL;
@@ -2176,9 +2432,9 @@
 void idetape_create_mode_sense_cmd (idetape_packet_command_t *pc,byte page_code)
 
 {
-#if IDETAPE_DEBUG
-		printk ("ide-tape: Creating MODE SENSE packet command - Page %d\n",page_code);
-#endif /* IDETAPE_DEBUG */
+#if IDETAPE_DEBUG_LOG
+	printk ("ide-tape: Creating MODE SENSE packet command - Page %d\n",page_code);
+#endif /* IDETAPE_DEBUG_LOG */
 
 	pc->wait_for_dsc=0;
 	pc->callback=&idetape_pc_callback;
@@ -2209,11 +2465,11 @@
 void idetape_create_write_filemark_cmd (idetape_packet_command_t *pc,int write_filemark)
 
 {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Creating WRITE FILEMARK packet command\n");
 	if (!write_filemark)
 		printk ("which will only flush buffered data\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	pc->request_transfer=0;
 	pc->buffer=NULL;
 	pc->current_position=NULL;
@@ -2228,13 +2484,32 @@
 		pc->c [4]=1;
 }
 
+void idetape_create_load_unload_cmd (idetape_packet_command_t *pc,int cmd)
+
+{
+#if IDETAPE_DEBUG_LOG
+	printk ("Creating LOAD UNLOAD packet command, cmd=%d\n",cmd);
+#endif /* IDETAPE_DEBUG_LOG */
+	pc->request_transfer=0;
+	pc->buffer=NULL;
+	pc->current_position=NULL;
+	pc->buffer_size=0;
+	pc->wait_for_dsc=1;
+	pc->callback=&idetape_pc_callback;
+	pc->writing=0;
+		
+	idetape_zero_packet_command (pc);
+	pc->c [0]=IDETAPE_LOAD_UNLOAD_CMD;
+	pc->c [4]=cmd;
+}
+
 void idetape_create_erase_cmd (idetape_packet_command_t *pc)
 
 {
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Creating ERASE command\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	pc->request_transfer=0;
 	pc->buffer=NULL;
@@ -2262,9 +2537,9 @@
 		} b;
 	} original;
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating READ packet command\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	original.all=length;
 
@@ -2295,9 +2570,9 @@
 		} b;
 	} original;
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating SPACE packet command\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	original.all=count;
 
@@ -2332,9 +2607,9 @@
 		} b;
 	} original;
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating WRITE packet command\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	original.all=length;
 
@@ -2355,9 +2630,9 @@
 void idetape_create_read_position_cmd (idetape_packet_command_t *pc)
 
 {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Creating READ POSITION packet command\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	pc->request_transfer=20;
 	pc->wait_for_dsc=0;
@@ -2378,20 +2653,20 @@
 	
 	tape=&(drive->tape);
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Reached idetape_read_position_callback\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	rq=HWGROUP(drive)->rq;
 	
 	if (!tape->pc->error) {
 		result=(idetape_read_position_result_t *) tape->pc->buffer;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("Request completed\n");
 		printk ("Dumping the results of the READ POSITION command\n");
 		printk ("BOP - %s\n",result->bop ? "Yes":"No");
 		printk ("EOP - %s\n",result->eop ? "Yes":"No");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		if (result->bpu) {
 			printk ("ide-tape: Block location is unknown to the tape\n");
 			printk ("Aborting request\n");
@@ -2399,9 +2674,9 @@
 			idetape_end_request (0,HWGROUP (drive));
 		}
 		else {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 			printk ("Block Location - %lu\n",idetape_swap_long (result->first_block));
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 			tape->block_address=idetape_swap_long (result->first_block);
 			tape->block_address_valid=1;
 			idetape_end_request (1,HWGROUP (drive));
@@ -2432,23 +2707,23 @@
 	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
 	pc.current_position=pc.temp_buffer;
 
-#if IDETAPE_DEBUG	
+#if IDETAPE_DEBUG_LOG	
 	printk ("ide-tape: Reached idetape_blkdev_ioctl\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	switch (cmd) {
 		case IDETAPE_INQUIRY_IOCTL:
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 			printk ("Adding INQUIRY packet command to the tail of the request queue\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 			idetape_create_inquiry_cmd (&pc);
 			pc.buffer=pc.temp_buffer;
 			pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
 			pc.current_position=pc.temp_buffer;
 			return (idetape_queue_pc_tail (drive,&pc));
 		case IDETAPE_LOCATE_IOCTL:
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 			printk ("Adding LOCATE packet command to the tail of the request queue\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 			idetape_create_locate_cmd (&pc,arg,0);
 			retval=idetape_queue_pc_tail (drive,&pc);
 			if (retval!=0) return (retval);
@@ -2474,10 +2749,6 @@
 
 /*
  *	idetape_end_request is used to end a request.
- *
- *	It is very similiar to ide_end_request, with a major difference - If
- *	we are handling our own requests rather than requests which originate
- *	in the buffer cache, we set rq->errors to 1 if the request failed.
  */
 
 void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
@@ -2486,51 +2757,58 @@
 	ide_drive_t *drive = hwgroup->drive;
 	struct request *rq = hwgroup->rq;
 	idetape_tape_t *tape = &(drive->tape);
-	
-							/* Our own originated request */
-	rq->errors=!uptodate;				/* rq->errors will tell us if the request was successfull */
-	
-	if (tape->active_data_request == rq) {		/* The request was a data transfer request */
-#if IDETAPE_DEBUG
-		if (!tape->max_number_of_stages)
-			printk ("ide-tape: non pipelined mode bug\n");
-		printk ("Finished our active data request\n");
-		printk ("Requests in pipeline: %d\n",tape->current_number_of_stages);
-#endif /* IDETAPE_DEBUG */
-		if (rq->errors)
-			tape->error_in_pipeline_stage=1;
+	unsigned int major = HWIF(drive)->major;
+	struct blk_dev_struct *bdev = &blk_dev[major];
 
-		/*
-		 *	Pass to the next stage, but avoid a possible
-		 *	race condition which could occur since
-		 *	active_data_request is set to NULL until
-		 *	idetape_active_next_stage returns, and in that
-		 *	time, the higher level of the driver can get
-		 *	an inaccurate sampling of this variable.
-		 */
-		 
-		tape->pipeline_locked=1;
-		tape->active_data_request = NULL;
-		if (tape->first_stage == NULL) {
-			tape->pipeline_locked=0;
+#if IDETAPE_DEBUG_LOG
+	printk ("Reached idetape_end_request\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+	bdev->current_request=rq;			/* Since we may have taken it out */
+
+	if (!rq->errors)				/* In case rq->errors is already set, */
+		rq->errors=!uptodate;			/* we won't change it. */
+	
+	if (tape->active_data_request == rq) {		/* The request was a pipelined data transfer request */
+
+		if (rq->cmd == IDETAPE_READ_REQUEST) {
+#if IDETAPE_DEBUG_BUGS
+			if (tape->active_stage == NULL)
+				printk ("ide-tape: bug: active_stage is NULL in idetape_end_request\n");
+			else				
+#endif /* IDETAPE_DEBUG_BUGS */
+			idetape_copy_buffer_to_stage (tape->active_stage,tape->data_buffer);
+		}
+
+		tape->active_stage=NULL;
+		tape->active_data_request=NULL;
+
+		if (rq->cmd == IDETAPE_WRITE_REQUEST) {
+			if (rq->errors)
+				tape->error_in_pipeline_stage=1;
+			idetape_remove_stage_head (drive);
+		}
+		
+		if (tape->next_stage == NULL) {
 			idetape_increase_max_pipeline_stages (drive);
+			ide_end_drive_cmd (drive, 0, 0);
 			return;
 		}
+
 		idetape_active_next_stage (drive);
-		tape->pipeline_locked=0;
-#if IDETAPE_DEBUG
-		printk ("Using ide_end\n");
-#endif /* IDETAPE_DEBUG */
 
 		/*
 		 *	Insert the next request into the request queue.
 		 *
-		 *	We currently give higher priority to the other devie
-		 *	by using ide_end. ide_next can be used to give us
-		 *	a higher priority.
+		 *	The choice of using ide_next or ide_end is now left
+		 *	to the user.
 		 */
 		 
-		ide_do_drive_cmd (drive,tape->active_data_request,ide_end);
+#if IDETAPE_LOW_TAPE_PRIORITY
+		(void) ide_do_drive_cmd (drive,tape->active_data_request,ide_end);
+#else
+		(void) ide_do_drive_cmd (drive,tape->active_data_request,ide_next);
+#endif /* IDETAPE_LOW_TAPE_PRIORITY */
 	}
 	ide_end_drive_cmd (drive, 0, 0);
 }
@@ -2544,13 +2822,15 @@
 {
 	idetape_tape_t *tape=&(drive->tape);
 	idetape_packet_command_t *pc;
+	unsigned int major = HWIF(drive)->major;
+	struct blk_dev_struct *bdev = &blk_dev[major];
 	idetape_status_reg_t status;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Current request:\n");
 	printk ("rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
 	printk ("sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	if (!IDETAPE_REQUEST_CMD (rq->cmd)) {
 
@@ -2569,6 +2849,29 @@
 		return;
 	}
 
+	/*
+	 *	This is an important point. We will try to remove our request
+	 *	from the block device request queue while we service the
+	 *	request. Note that the request must be returned to
+	 *	bdev->current_request before the next call to
+	 *	ide_end_drive_cmd or ide_do_drive_cmd to conform with the
+	 *	normal behavior of the IDE driver, which leaves the active
+	 *	request in bdev->current_request during I/O.
+	 *
+	 *	This will eliminate fragmentation of disk/cdrom requests
+	 *	around a tape request, now that we are using ide_next to
+	 *	insert pending pipeline requests, since we have only one
+	 *	ide-tape.c data request in the device request queue, and
+	 *	thus once removed, ll_rw_blk.c will only see requests from
+	 *	the other device.
+	 *
+	 *	The potential fragmentation inefficiency was pointed to me
+	 *	by Mark Lord.
+	 */
+	 
+	if (rq->next != NULL && rq->rq_dev != rq->next->rq_dev)
+		bdev->current_request=rq->next;
+
 	/* Retry a failed packet command */
 
 	if (tape->failed_pc != NULL && tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
@@ -2579,13 +2882,13 @@
 	/* Check if we have a postponed request */
 	
 	if (tape->postponed_rq != NULL) {
- #if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_BUGS
 		if (tape->postponed_rq->rq_status != RQ_ACTIVE || rq != tape->postponed_rq) {
 			printk ("ide-tape: ide-tape.c bug - Two DSC requests were queued\n");
 			idetape_end_request (0,HWGROUP (drive));
 			return;
 		}
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 		if (rq->cmd == IDETAPE_PACKET_COMMAND_REQUEST_TYPE1) {
 	
 			/* Media access command */
@@ -2606,15 +2909,12 @@
 	
 	switch (rq->cmd) {
 		case IDETAPE_READ_REQUEST:
-#if IDETAPE_DEBUG
-			printk ("ide-tape: Handling our own (not buffer cache originated) READ request\n");
-#endif /* IDETAPE_DEBUG */			
 			status.all=IN_BYTE (IDETAPE_STATUS_REG);
 			if (!status.b.dsc) {				/* Tape buffer not ready to accept r/w command */
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 				printk ("ide-tape: DSC != 1 - Postponing read request\n");
-#endif /* IDETAPE_DEBUG */	
-				tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FREQUENCY;
+#endif /* IDETAPE_DEBUG_LOG */	
+				tape->dsc_polling_frequency=tape->best_dsc_rw_frequency;
 				idetape_postpone_request (drive);	/* Allow ide.c to process requests from */
 				return;
 			}			
@@ -2632,16 +2932,12 @@
 			return;
 		
 		case IDETAPE_WRITE_REQUEST:
-#if IDETAPE_DEBUG
-			printk ("ide-tape: Handling our own (not buffer cache originated) WRITE request\n");
-#endif /* IDETAPE_DEBUG */			
-
 			status.all=IN_BYTE (IDETAPE_STATUS_REG);
 			if (!status.b.dsc) {				/* Tape buffer not ready to accept r/w command */
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 				printk ("ide-tape: DSC != 1 - Postponing write request\n");
-#endif /* IDETAPE_DEBUG */	
-				tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FREQUENCY;
+#endif /* IDETAPE_DEBUG_LOG */	
+				tape->dsc_polling_frequency=tape->best_dsc_rw_frequency;
 				idetape_postpone_request (drive);	/* Allow ide.c to process requests from */
 				return;
 			}			
@@ -2667,12 +2963,12 @@
  */
 			status.all=IN_BYTE (IDETAPE_STATUS_REG);
 			if (!status.b.dsc) {				/* Tape buffers are still not ready */
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 				printk ("ide-tape: DSC != 1 - Postponing packet command request\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 				rq->cmd=IDETAPE_PACKET_COMMAND_REQUEST_TYPE2;	/* Note that we are waiting for DSC *before* we */
 										/* even issued the command */
-				tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FREQUENCY;
+				tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FALLBACK_FREQUENCY;
 				idetape_postpone_request (drive);	/* Allow ide.c to process requests from */
 				return;
 			}
@@ -2680,11 +2976,11 @@
 			pc=(idetape_packet_command_t *) rq->buffer;
 			idetape_issue_packet_command (drive,pc,&idetape_pc_intr);
 			return;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_BUGS
 		default:
 			printk ("ide-tape: bug in IDETAPE_REQUEST_CMD macro\n");
 			idetape_end_request (0,HWGROUP (drive));
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 	}	
 }
 
@@ -2742,22 +3038,38 @@
 void idetape_queue_pc_head (ide_drive_t *drive,idetape_packet_command_t *pc,struct request *rq)
 
 {
+	unsigned int major = HWIF(drive)->major;
+	struct blk_dev_struct *bdev = &blk_dev[major];
+
+	bdev->current_request=HWGROUP (drive)->rq;		/* Since we may have taken it out */
+
 	ide_init_drive_cmd (rq);
 	rq->buffer = (char *) pc;
 	rq->cmd = IDETAPE_PACKET_COMMAND_REQUEST_TYPE1;
 	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
 }
 
+/*
+ *	idetape_wait_for_request installs a semaphore in a pending request
+ *	and sleeps until it is serviced.
+ *
+ *	The caller should ensure that the request will not be serviced
+ *	before we install the semaphore (usually by disabling interrupts).
+ */
+ 
 void idetape_wait_for_request (struct request *rq)
 
 {
-	unsigned long flags;
 	struct semaphore sem = MUTEX_LOCKED;
 
-	save_flags (flags);cli ();
-	rq->sem=&sem;
-	restore_flags (flags);
+#if IDETAPE_DEBUG_BUGS
+	if (rq == NULL || !IDETAPE_REQUEST_CMD (rq->cmd)) {
+		printk ("ide-tape: bug: Trying to sleep on non-valid request\n");
+		return;		
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
 
+	rq->sem=&sem;
 	down (&sem);
 }
 
@@ -2777,9 +3089,9 @@
 	idetape_tape_t *tape = &(drive->tape);
 	struct request rq;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("idetape_queue_rw_tail: cmd=%d\n",cmd);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	/* build up a special read request, and add it to the queue */
 	
 	ide_init_drive_cmd (&rq);
@@ -2789,35 +3101,92 @@
 	rq.nr_sectors = blocks;
 	rq.current_nr_sectors = blocks;
 	tape->active_data_request=NULL;	/* Non-pipelined mode */
-#if IDETAPE_DEBUG
-		printk ("Using ide_tail\n");
-#endif /* IDETAPE_DEBUG */
 	return ide_do_drive_cmd (drive, &rq, ide_wait);
 }
 
- /*
- *	idetape_add_chrdev_write_request tries to add a character device
- *	originated write request to our pipeline. In case we don't succeed,
- *	we revert to non-piplined operation mode for this request.
- *
- *	1.	Try to allocate a new pipeline stage.
- *	2.	If we can't, wait for more and more requests to be serviced
- *		and try again each time.
- *	3.	If we still can't allocate a stage, fallback to
- *		non-pipelined operation mode for this request.
+/*
+ *	idetape_add_chrdev_read_request handles character device read requests
+ *	when operating in the pipelined operation mode.
+ */
+ 
+int idetape_add_chrdev_read_request (ide_drive_t *drive,int blocks,char *buffer)
+
+{
+	idetape_tape_t *tape = &(drive->tape);
+	idetape_pipeline_stage_t *new_stage;
+	unsigned long flags;
+	struct request rq;
+	int errors;
+	
+#if IDETAPE_DEBUG_LOG
+	printk ("Reached idetape_add_chrdev_read_request\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+	ide_init_drive_cmd (&rq);
+	rq.cmd = IDETAPE_READ_REQUEST;
+	rq.sector = tape->block_address;
+	rq.nr_sectors = blocks;
+	rq.current_nr_sectors = blocks;
+
+	if (tape->current_number_of_stages < 0.5*tape->max_number_of_stages) {
+		new_stage=idetape_kmalloc_stage (drive);
+		while (new_stage != NULL) {
+			new_stage->rq=rq;
+			save_flags (flags);cli ();
+			idetape_add_stage_tail (drive,new_stage);
+			restore_flags (flags);
+			new_stage=idetape_kmalloc_stage (drive);
+		}
+		if (tape->active_data_request == NULL)
+			idetape_insert_pipeline_into_queue (drive);
+	}
+
+	if (tape->first_stage == NULL) {
+
+		/*
+		 *	Linux is short on memory. Revert to non-pipelined
+		 *	operation mode for this request.
+		 */
+		 
+		return (idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,blocks,buffer));
+	}		
+	
+	save_flags (flags);cli ();
+	if (tape->active_data_request == &(tape->first_stage->rq))
+		idetape_wait_for_request (tape->active_data_request);
+	restore_flags (flags);
+
+	errors=tape->first_stage->rq.errors;
+	if (!errors)		
+		idetape_copy_buffer_from_stage (tape->first_stage,buffer);
+
+	idetape_remove_stage_head (drive);
+	return (errors ? -EIO:0);
+}
+
+/*
+ *	idetape_add_chrdev_write_request tries to add a character device
+ *	originated write request to our pipeline. In case we don't succeed,
+ *	we revert to non-piplined operation mode for this request.
+ *
+ *	1.	Try to allocate a new pipeline stage.
+ *	2.	If we can't, wait for more and more requests to be serviced
+ *		and try again each time.
+ *	3.	If we still can't allocate a stage, fallback to
+ *		non-pipelined operation mode for this request.
  */
 
-int idetape_add_chrdev_write_request (ide_drive_t *drive,int cmd,int blocks,char *buffer)
+int idetape_add_chrdev_write_request (ide_drive_t *drive,int blocks,char *buffer)
 
 {
 	idetape_tape_t *tape = &(drive->tape);
 	idetape_pipeline_stage_t *new_stage;
+	unsigned long flags;
 	struct request *rq;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_add_chrdev_write_request\n");
-	printk ("Trying to allocate stage - ");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	
 	if (tape->error_in_pipeline_stage)		/* Return a deferred error */
 		return (-EIO);
@@ -2832,66 +3201,109 @@
 	 */
 
 	while (new_stage == NULL) {
-
-		/*
-		 *	Wait for the time in which we can safely inspect
-		 *	tape->first_stage.
-		 */
-		 
-		while (tape->pipeline_locked);
-
-		if (tape->first_stage != NULL) {
-			idetape_wait_for_request (&(tape->first_stage->rq));
+		save_flags (flags);cli ();
+		if (tape->active_data_request != NULL) {
+			idetape_wait_for_request (tape->active_data_request);
+			restore_flags (flags);
 			new_stage=idetape_kmalloc_stage (drive);
 		}
-		else
-			break;					/* Linux is short on memory */
-	}
-	
-	/*
-	 *	If we don't have a new_stage, fallback to non-pipelined
-	 *	operation mode for this request.
-	 */
+		else {
+			/*
+			 *	Linux is short on memory. Fallback to
+			 *	non-pipelined operation mode for this request.
+			 */
 	 	 	
-	if (new_stage == NULL) {
-		if (tape->active_data_request != NULL)
-			idetape_wait_for_request (tape->active_data_request);
-		return (idetape_queue_rw_tail (drive,cmd,blocks,buffer));
+			restore_flags (flags);
+			return (idetape_queue_rw_tail (drive,IDETAPE_WRITE_REQUEST,blocks,buffer));
+		}
 	}
 
 	rq=&(new_stage->rq);
 
 	ide_init_drive_cmd (rq);
-	rq->buffer = NULL;			/* We will correct this when we will actually service the request */
-	rq->cmd = cmd;
+	rq->cmd = IDETAPE_WRITE_REQUEST;
 	rq->sector = tape->block_address;	/* Doesn't actually matter - We always assume sequential access */
 	rq->nr_sectors = blocks;
 	rq->current_nr_sectors = blocks;
 
 	idetape_copy_buffer_to_stage (new_stage,buffer);
+
+	save_flags (flags);cli ();
 	idetape_add_stage_tail (drive,new_stage);
+	restore_flags (flags);
+
+	/*
+	 *	Check if we are currently servicing requests in the bottom
+	 *	part of the driver.
+	 *
+	 *	If not, wait for the pipeline to be full enough (75%) before
+	 *	starting to service requests, so that we will be able to
+	 *	keep up with the higher speeds of the tape.
+	 */
+
+	if (tape->active_data_request == NULL && tape->current_number_of_stages >= 0.75*tape->max_number_of_stages)
+		idetape_insert_pipeline_into_queue (drive);		
 
 	return (0);		
 }
 
-void idetape_empty_pipeline (ide_drive_t *drive)
+void idetape_empty_read_pipeline (ide_drive_t *drive)
 
 {
 	idetape_tape_t *tape = &(drive->tape);
+	unsigned long flags;
 
-	tape->pipeline_was_full_once=0;
+#if IDETAPE_DEBUG_BUGS
+	if (tape->chrdev_direction != idetape_direction_read) {
+		printk ("ide-tape: bug: Trying to empty read pipeline, but we are not reading.\n");
+		return;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
+
+	if (tape->first_stage == NULL)
+		return;
+		
+	save_flags (flags);cli ();
+	tape->next_stage=NULL;
+	if (tape->active_data_request != NULL)
+		idetape_wait_for_request (tape->active_data_request);
+	restore_flags (flags);
+
+	while (tape->first_stage != NULL)
+		idetape_remove_stage_head (drive);
+
+#if IDETAPE_PIPELINE
+	tape->max_number_of_stages=IDETAPE_MIN_PIPELINE_STAGES;
+#else
+	tape->max_number_of_stages=0;
+#endif /* IDETAPE_PIPELINE */
+}
+
+
+void idetape_empty_write_pipeline (ide_drive_t *drive)
+
+{
+	idetape_tape_t *tape = &(drive->tape);
+	unsigned long flags;
+
+#if IDETAPE_DEBUG_BUGS
+	if (tape->chrdev_direction != idetape_direction_write) {
+		printk ("ide-tape: bug: Trying to empty write pipeline, but we are not writing.\n");
+		return;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
 
-	while (tape->pipeline_locked);
-	
 	if (tape->active_data_request == NULL)
 		idetape_insert_pipeline_into_queue (drive);		
 
+	save_flags (flags);cli ();
 	if (tape->last_stage != NULL)
 		idetape_wait_for_request (&(tape->last_stage->rq));
 
 	else if (tape->active_data_request != NULL)
 		idetape_wait_for_request (tape->active_data_request);
-
+	restore_flags (flags);
+	
 	tape->error_in_pipeline_stage=0;
 
 	/*
@@ -2907,11 +3319,11 @@
 	tape->max_number_of_stages=0;
 #endif /* IDETAPE_PIPELINE */
 
-#if IDETAPE_DEBUG
-	if (tape->first_stage != NULL || tape->last_stage != NULL || tape->current_number_of_stages != 0) {
+#if IDETAPE_DEBUG_BUGS
+	if (tape->first_stage != NULL || tape->next_stage != NULL || tape->last_stage != NULL || tape->current_number_of_stages != 0) {
 		printk ("ide-tape: ide-tape pipeline bug\n");		
 	}
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */
 }
 
 
@@ -2928,6 +3340,7 @@
 	for (i=0;i<12;i++)
 		pc->c[i]=0;
 	pc->retries=0;
+	pc->abort=0;
 }
 
 /*
@@ -2991,9 +3404,9 @@
 	idetape_tape_t *tape;
 	
 	tape=&(drive->tape);
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: pc_stack_index=%d\n",tape->pc_stack_index);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	if (tape->pc_stack_index==IDETAPE_PC_STACK)
 		tape->pc_stack_index=0;
 	return (&(tape->pc_stack [tape->pc_stack_index++]));
@@ -3019,9 +3432,9 @@
 	
 	tape=&(drive->tape);
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: rq_stack_index=%d\n",tape->rq_stack_index);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	if (tape->rq_stack_index==IDETAPE_PC_STACK)
 		tape->rq_stack_index=0;
 	return (&(tape->rq_stack [tape->rq_stack_index++]));
@@ -3041,11 +3454,11 @@
 	idetape_tape_t *tape=&(drive->tape);
 	unsigned long flags;
 			
-	save_flags (flags);cli();
+	save_flags (flags);cli ();
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_blkdev_open\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	if (tape->busy) {
 		restore_flags (flags);		/* Allowing access only through one */
@@ -3064,11 +3477,11 @@
 	idetape_tape_t *tape=&(drive->tape);
 	unsigned long flags;
 			
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_blkdev_release\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
-	save_flags (flags);cli();
+	save_flags (flags);cli ();
 	tape->busy=0;
 	restore_flags (flags);
 
@@ -3080,28 +3493,6 @@
  */
 
 /*
- *	lseek is currently not installed.
- */
- 
-int idetape_chrdev_lseek (struct inode *inode, struct file *file, off_t offset, int origin)
-
-{
-	ide_drive_t *drive;
-	
-#if IDETAPE_DEBUG
-	printk ("Reached idetape_chrdev_lseek\n");
-#endif /* IDETAPE_DEBUG */
-
-	drive=idetape_chrdev.drive;
-	if (idetape_position_tape (drive,offset) != 0) {
-		printk ("ide-tape: Rewinding tape failed\n");
-		return (-1);
-	}
-	
-	return (0);
-}
-
-/*
  *	Our character device read / write functions.
  *
  *	The tape is optimized to maximize throughput when it is transfering
@@ -3120,8 +3511,8 @@
  *	after this request.
  *
  *	Again, for best results use an integral number of the tape's parameter
- *	(which is displayed in the driver installation stage). I will soon
- *	add an ioctl to get this important parameter.
+ *	(which is displayed in the driver installation stage and is returned
+ *	 by the MTIOCGET ioctl).
  */
 
 /*
@@ -3131,47 +3522,59 @@
 int idetape_chrdev_read (struct inode *inode, struct file *file, char *buf, int count)
 
 {
-	ide_drive_t *drive;
-	idetape_tape_t *tape;
-	int blocks,remainder,retval,ctl_bytes;
+	ide_drive_t *drive=idetape_chrdev.drive;
+	idetape_tape_t *tape=&(drive->tape);
+	int blocks,remainder,retval;
 	char *buf_ptr;
 	unsigned long previous_block_address,actually_read;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_read\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
-	drive=idetape_chrdev.drive;
-	tape=&(drive->tape);
-	tape->last_dt_was_write=0;
+	if (tape->chrdev_direction != idetape_direction_read) {		/* Initialize read operation */
+		if (tape->chrdev_direction == idetape_direction_write) {
+			idetape_empty_write_pipeline (drive);
+			idetape_flush_tape_buffers (drive);
+		}
+		
+		/*
+		 *	Issue a read 0 command to ensure that DSC handshake
+		 *	is switched from completion mode to buffer available
+		 *	mode.
+		 */
+		 
+		retval=idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,0,tape->temp_data_buffer);
+		if (retval)
+			return (retval);
 
+		tape->chrdev_direction=idetape_direction_read;
+	}
+	
 	if (count==0)
 		return (0);
 
 	actually_read=0;
 	buf_ptr=buf;
-	ctl_bytes=tape->capabilities.ctl*tape->tape_block_size;
-	blocks=count/ctl_bytes;
-	remainder=count%ctl_bytes;
+	blocks=count/tape->data_buffer_size;
+	remainder=count%tape->data_buffer_size;
 	
 	while (blocks) {
-#if IDETAPE_DEBUG
-		printk ("Adding a READ request to the block device request queue\n");
-#endif /* IDETAPE_DEBUG */
 		previous_block_address=tape->block_address;
-		retval=idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,tape->capabilities.ctl,tape->data_buffer);
-		actually_read+=tape->tape_block_size*(tape->block_address-previous_block_address);
+		retval=idetape_add_chrdev_read_request (drive,tape->capabilities.ctl,tape->temp_data_buffer);
+		if (tape->max_number_of_stages)
+			actually_read+=tape->data_buffer_size;
+		else
+			actually_read+=tape->tape_block_size*(tape->block_address-previous_block_address);
 
 		if (retval) {
-			printk ("ide-tape: Error occured while reading\n");
-			return (actually_read);
+			if (tape->max_number_of_stages)
+				return (0);
+			else
+				return (actually_read);
 		}
-#if IDETAPE_DEBUG
-	printk ("Copying %d bytes to the user space memory\n",ctl_bytes);
-#endif /* IDETAPE_DEBUG */
-
-		memcpy_tofs (buf_ptr,tape->data_buffer,ctl_bytes);
-		buf_ptr+=ctl_bytes;
+		memcpy_tofs (buf_ptr,tape->temp_data_buffer,tape->data_buffer_size);
+		buf_ptr+=tape->data_buffer_size;
 		blocks--;
 	}
 	if (remainder)
@@ -3183,49 +3586,43 @@
 int idetape_chrdev_read_remainder (struct inode *inode, struct file *file, char *buf, int count)
 
 {
-	ide_drive_t *drive;
-	idetape_tape_t *tape;
+	ide_drive_t *drive=idetape_chrdev.drive;
+	idetape_tape_t *tape=&(drive->tape);
 	int blocks,remainder,retval;
 	unsigned long previous_block_address,actually_read;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_read_remainder\n");
-#endif /* IDETAPE_DEBUG */
-
-	drive=idetape_chrdev.drive;
-	tape=&(drive->tape);
-
-	tape->last_dt_was_write=0;
-
-	if (count==0)
-		return (0);
+#endif /* IDETAPE_DEBUG_LOG */
 
 	blocks=count/tape->tape_block_size;
 	remainder=count%tape->tape_block_size;
 	if (remainder) {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Padding read to block boundary\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		blocks++;
 	}
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Adding a READ request to the block device request queue\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	previous_block_address=tape->block_address;
-	retval=idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,blocks,tape->data_buffer);
+	retval=idetape_add_chrdev_read_request (drive,blocks,tape->temp_data_buffer);
 	if (retval) {
-		printk ("ide-tape: Error occured while reading\n");
-		actually_read=tape->tape_block_size*(tape->block_address-previous_block_address);
+		if (tape->max_number_of_stages)
+			actually_read=0;
+		else
+			actually_read=tape->tape_block_size*(tape->block_address-previous_block_address);
 		if (actually_read > count)
 			actually_read=count;
 		if (actually_read != 0)
-			memcpy_tofs (buf,tape->data_buffer,actually_read);
+			memcpy_tofs (buf,tape->temp_data_buffer,actually_read);
 		return (actually_read);
 	}
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Copying %d bytes to the user space memory\n",count);
-#endif /* IDETAPE_DEBUG */
-	memcpy_tofs (buf,tape->data_buffer,count);
+#endif /* IDETAPE_DEBUG_LOG */
+	memcpy_tofs (buf,tape->temp_data_buffer,count);
 	return (count);
 }
 
@@ -3238,14 +3635,29 @@
 	const char *buf_ptr;
 	unsigned long previous_block_address,actually_written;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_write\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	drive=idetape_chrdev.drive;
 	tape=&(drive->tape);
 
-	tape->last_dt_was_write=1;
+	if (tape->chrdev_direction != idetape_direction_write) {	/* Initialize write operation */
+		if (tape->chrdev_direction == idetape_direction_read)
+			idetape_empty_read_pipeline (drive);
+
+		/*
+		 *	Issue a write 0 command to ensure that DSC handshake
+		 *	is switched from completion mode to buffer available
+		 *	mode.
+		 */
+
+		retval=idetape_queue_rw_tail (drive,IDETAPE_WRITE_REQUEST,0,tape->temp_data_buffer);
+		if (retval)
+			return (retval);		
+	
+		tape->chrdev_direction=idetape_direction_write;
+	}
 
 	if (count==0)
 		return (0);
@@ -3259,14 +3671,13 @@
 		memcpy_fromfs (tape->temp_data_buffer,buf_ptr,tape->data_buffer_size);
 		buf_ptr+=tape->data_buffer_size;
 		previous_block_address=tape->block_address;
-		retval=idetape_add_chrdev_write_request (drive,IDETAPE_WRITE_REQUEST,tape->capabilities.ctl,tape->temp_data_buffer);
+		retval=idetape_add_chrdev_write_request (drive,tape->capabilities.ctl,tape->temp_data_buffer);
 		if (tape->max_number_of_stages)
 			actually_written+=tape->data_buffer_size;	/* Pipelined mode - Cheat :-) */
 		else
 			actually_written+=tape->tape_block_size*(tape->block_address-previous_block_address);
 
 		if (retval) {
-			printk ("ide-tape: Error occured while writing\n");
 			if (tape->max_number_of_stages)
 				return (0);
 			else
@@ -3289,9 +3700,9 @@
 	char *ptr;
 	unsigned long previous_block_address,actually_written;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_write_remainder\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		
 	drive=idetape_chrdev.drive;
 	tape=&(drive->tape);
@@ -3300,26 +3711,25 @@
 	remainder=count%tape->tape_block_size;
 	if (remainder)
 		blocks++;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Copying %d bytes from the user space memory\n",count);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	memcpy_fromfs (tape->temp_data_buffer,buf,count);
 	if (remainder) {
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("ide-tape: Padding written data to block boundary\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 		ptr=tape->temp_data_buffer+(blocks-1)*tape->tape_block_size;
 		memset (ptr,0,remainder);
 	}
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Adding a WRITE request to the block device request queue\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	previous_block_address=tape->block_address;
-	retval=idetape_add_chrdev_write_request (drive,IDETAPE_WRITE_REQUEST,blocks,tape->temp_data_buffer);
+	retval=idetape_add_chrdev_write_request (drive,blocks,tape->temp_data_buffer);
 	if (retval) {
-		printk ("ide-tape: Error occured while writing\n");
 		if (tape->max_number_of_stages)
 			actually_written=0;
 		else
@@ -3337,21 +3747,61 @@
  *	General mtio.h magnetic io commands are supported here, and not in
  *	the correspoding block interface.
  *
+ *	The following ioctls are supported:
+ *
+ *	MTIOCTOP -	Refer to idetape_mtioctop for detailed description.
+ *
+ *	MTIOCGET - 	The mt_dsreg field in the returned mtget structure
+ *			will be set to (recommended block size <<
+ *			MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK, which
+ *			is currently equal to the size itself.
+ *			The other mtget fields are not supported.
+ *
+ *			Note that we do not actually return the tape's
+ *			block size. Rather, we provide the recommended
+ *			number of bytes which sould be used as a "user
+ *			block size" with the character device read/write
+ *			functions to maximize throughput.
+ *
+ *	MTIOCPOS -	The current tape "position" is returned.
+ *			(A unique number which can be used with the MTSEEK
+ *			 operation to return to this position in some
+ *			 future time, provided this place was not overwritten
+ *			 meanwhile).
+ *
  *	Our own ide-tape ioctls are supported on both interfaces.
  */
 
 int idetape_chrdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 
 {
+	ide_drive_t *drive=idetape_chrdev.drive;
+	idetape_tape_t *tape=&(drive->tape);
+	idetape_packet_command_t pc;
 	struct mtop mtop;
-	ide_drive_t *drive;
+	struct mtget mtget;
+	struct mtpos mtpos;
 	int retval;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_ioctl, cmd=%u\n",cmd);
-#endif
+#endif /* IDETAPE_DEBUG_LOG */
 
-	drive=idetape_chrdev.drive;
+	if (tape->chrdev_direction == idetape_direction_write) {
+		idetape_empty_write_pipeline (drive);
+		idetape_flush_tape_buffers (drive);
+		tape->chrdev_direction=idetape_direction_none;
+	}
+
+	if (tape->chrdev_direction == idetape_direction_read)
+		if (cmd != MTIOCTOP) {
+			idetape_empty_read_pipeline (drive);
+			tape->chrdev_direction=idetape_direction_none;
+		}
+	
+	pc.buffer=pc.temp_buffer;
+	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+	pc.current_position=pc.temp_buffer;
 
 	switch (cmd) {
 		case MTIOCTOP:
@@ -3359,6 +3809,21 @@
 			if (retval) return (retval);
 			memcpy_fromfs ((char *) &mtop, (char *) arg, sizeof (struct mtop));
 			return (idetape_mtioctop (drive,mtop.mt_op,mtop.mt_count));
+		case MTIOCGET:
+			mtget.mt_dsreg=(tape->data_buffer_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK;
+			retval=verify_area (VERIFY_WRITE,(char *) arg,sizeof (struct mtget));
+			if (retval) return (retval);
+			memcpy_tofs ((char *) arg,(char *) &mtget, sizeof (struct mtget));
+			return (0);
+		case MTIOCPOS:
+			idetape_create_read_position_cmd (&pc);
+			retval=idetape_queue_pc_tail (drive,&pc);
+			if (retval) return (retval);
+			mtpos.mt_blkno=tape->block_address;
+			retval=verify_area (VERIFY_WRITE,(char *) arg,sizeof (struct mtpos));
+			if (retval) return (retval);
+			memcpy_tofs ((char *) arg,(char *) &mtpos, sizeof (struct mtpos));
+			return (0);
 		default:
 			return (idetape_blkdev_ioctl (drive,inode,file,cmd,arg));
 	}
@@ -3381,70 +3846,82 @@
  *
  *	MTBSFM	-	Like MTBSF, only tape is positioned after the last filemark.
  *
+ *
+ *	Note:
+ *
+ *		MTBSF and MTBSFM are not supported when the tape doesn't
+ *		supports spacing over filemarks in the reverse direction.
+ *		In this case, MTFSFM is also usually not supported (it is
+ *		supported in the rare case in which we crossed the filemark
+ *		during our read-ahead pipelined operation mode).
+ *		
  *	MTWEOF	-	Writes mt_count filemarks. Tape is positioned after
  *			the last written filemark.
  *
  *	MTREW	-	Rewindes tape.
  *
+ *	MTOFFL	-	Puts the tape drive "Offline": Rewinds the tape and
+ *			prevents further access until the media is replaced.
+ *
  *	MTNOP	-	Flushes tape buffers.
  *
+ *	MTRETEN	-	Retension media. This typically consists of one end
+ *			to end pass on the media.
+ *
  *	MTEOM	-	Moves to the end of recorded data.
  *
  *	MTERASE	-	Erases tape.
  *
+ *	MTSEEK	-	Positions the tape in a specific block number, which
+ *			was previously received using the MTIOCPOS ioctl,
+ *			assuming this place was not overwritten meanwhile.
+ *
  *	The following commands are currently not supported:
  *
- *	MTFSR, MTBSR, MTFSS, MTBSS, MTWSM, MTOFFL, MTRETEN, MTSEEK, MTSETBLK,
- *	MTSETDENSITY, MTSETDRVBUFFER, MT_ST_BOOLEANS, MT_ST_WRITE_THRESHOLD.
+ *	MTFSR, MTBSR, MTFSS, MTBSS, MTWSM, MTSETBLK, MTSETDENSITY,
+ *	MTSETDRVBUFFER, MT_ST_BOOLEANS, MT_ST_WRITE_THRESHOLD.
  */
  
 int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count)
 
 {
-	int i,retval;
-	
+	idetape_tape_t *tape=&(drive->tape);
 	idetape_packet_command_t pc;
+	int i,retval;
 
 	pc.buffer=pc.temp_buffer;
 	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
 	pc.current_position=pc.temp_buffer;
 
-	idetape_create_write_filemark_cmd (&pc,0);	/* Flush buffers */
-	retval=idetape_queue_pc_tail (drive,&pc);
-	if (retval) return (retval);
+#if IDETAPE_DEBUG_LOG
+	printk ("Handling MTIOCTOP ioctl: mt_op=%d, mt_count=%d\n",mt_op,mt_count);
+#endif /* IDETAPE_DEBUG_LOG */
+
+	/*
+	 *	Commands which need our pipelined read-ahead stages.
+	 */
 
 	switch (mt_op) {
 		case MTFSF:
-#if IDETAPE_DEBUG
-			printk ("Handling MTFSF command\n");
-#endif /* IDETAPE_DEBUG */
-			idetape_create_space_cmd (&pc,mt_count,IDETAPE_SPACE_OVER_FILEMARK);
-			return (idetape_queue_pc_tail (drive,&pc));
 		case MTFSFM:
-#if IDETAPE_DEBUG
-			printk ("Handling MTFSFM command\n");
-#endif /* IDETAPE_DEBUG */
-			retval=idetape_mtioctop (drive,MTFSF,mt_count);
-			if (retval) return (retval);
-			return (idetape_mtioctop (drive,MTBSF,1));
 		case MTBSF:
-#if IDETAPE_DEBUG
-			printk ("Handling MTBSF command\n");
-#endif /* IDETAPE_DEBUG */
-			idetape_create_space_cmd (&pc,-mt_count,IDETAPE_SPACE_OVER_FILEMARK);
-			return (idetape_queue_pc_tail (drive,&pc));
 		case MTBSFM:
-#if IDETAPE_DEBUG
-			printk ("Handling MTBSFM command\n");
-#endif /* IDETAPE_DEBUG */
-			retval=idetape_mtioctop (drive,MTBSF,mt_count);
-			if (retval) return (retval);
-			return (idetape_mtioctop (drive,MTFSF,1));
+			return (idetape_space_over_filemarks (drive,mt_op,mt_count));
+		default:
+			break;
+	}
+
+	/*
+	 *	Empty the pipeline.
+	 */
+
+	if (tape->chrdev_direction == idetape_direction_read) {
+		idetape_empty_read_pipeline (drive);
+		tape->chrdev_direction=idetape_direction_none;
+	}
+
+	switch (mt_op) {
 		case MTWEOF:
-#if IDETAPE_DEBUG
-			printk ("Handling MTWEOF command\n");
-#endif /* IDETAPE_DEBUG */
-		
 			for (i=0;i<mt_count;i++) {
 				idetape_create_write_filemark_cmd (&pc,1);
 				retval=idetape_queue_pc_tail (drive,&pc);
@@ -3452,31 +3929,115 @@
 			}
 			return (0);
 		case MTREW:
-#if IDETAPE_DEBUG
-			printk ("Handling MTREW command\n");
-#endif /* IDETAPE_DEBUG */
 			return (idetape_rewind_tape (drive));
+		case MTOFFL:
+			idetape_create_load_unload_cmd (&pc,!IDETAPE_LU_LOAD_MASK);
+			return (idetape_queue_pc_tail (drive,&pc));
 		case MTNOP:
-#if IDETAPE_DEBUG
-			printk ("Handling MTNOP command\n");
-#endif /* IDETAPE_DEBUG */
-			idetape_create_write_filemark_cmd (&pc,0);
+			return (idetape_flush_tape_buffers (drive));
+		case MTRETEN:
+			idetape_create_load_unload_cmd (&pc,IDETAPE_LU_RETENSION_MASK | IDETAPE_LU_LOAD_MASK);
 			return (idetape_queue_pc_tail (drive,&pc));
 		case MTEOM:
-#if IDETAPE_DEBUG
-			printk ("Handling MTEOM command\n");
-#endif /* IDETAPE_DEBUG */
-		
 			idetape_create_space_cmd (&pc,0,IDETAPE_SPACE_TO_EOD);
 			return (idetape_queue_pc_tail (drive,&pc));
 		case MTERASE:
-#if IDETAPE_DEBUG
-			printk ("Handling MTERASE command\n");
-#endif /* IDETAPE_DEBUG */
-			retval=idetape_position_tape (drive,0);
+			retval=idetape_rewind_tape (drive);
 			if (retval) return (retval);
 			idetape_create_erase_cmd (&pc);
 			return (idetape_queue_pc_tail (drive,&pc));
+		case MTSEEK:
+			return (idetape_position_tape (drive,mt_count));
+		default:
+			printk ("ide-tape: MTIO operation %d not supported\n",mt_op);
+			return (-EIO);
+	}
+}
+
+/*
+ *	idetape_space_over_filemarks is now a bit more complicated than just
+ *	passing the command to the tape since we may have crossed some
+ *	filemarks during our pipelined read-ahead mode.
+ *
+ *	As a minor side effect, the pipeline enables us to support MTFSFM when
+ *	the filemark is in our internal pipeline even if the tape doesn't
+ *	support spacing over filemarks in the reverse direction.
+ */
+ 
+int idetape_space_over_filemarks (ide_drive_t *drive,short mt_op,int mt_count)
+
+{
+	idetape_tape_t *tape=&(drive->tape);
+	idetape_packet_command_t pc;
+	unsigned long flags;
+	int retval,count=0,errors;
+
+	if (tape->chrdev_direction == idetape_direction_read) {
+
+		/*
+		 *	We have a read-ahead buffer. Scan it for crossed
+		 *	filemarks.
+		 */
+		 
+		while (tape->first_stage != NULL) {
+			
+			/*
+			 *	Wait until the first read-ahead request
+			 *	is serviced.
+			 */
+		
+			save_flags (flags);cli ();
+			if (tape->active_data_request == &(tape->first_stage->rq))
+				idetape_wait_for_request (tape->active_data_request);
+			restore_flags (flags);
+
+			errors=tape->first_stage->rq.errors;
+			if (errors == IDETAPE_RQ_ERROR_FILEMARK)
+				count++;
+
+			if (count == mt_count) {
+				switch (mt_op) {
+					case MTFSF:
+						idetape_remove_stage_head (drive);
+					case MTFSFM:
+						return (0);
+				}
+			}
+			idetape_remove_stage_head (drive);
+		}
+		tape->chrdev_direction = idetape_direction_none;
+	}
+
+	/*
+	 *	The filemark was not found in our internal pipeline.
+	 *	Now we can issue the space command.
+	 */
+
+	pc.buffer=pc.temp_buffer;
+	pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+	pc.current_position=pc.temp_buffer;
+
+	switch (mt_op) {
+		case MTFSF:
+			idetape_create_space_cmd (&pc,mt_count-count,IDETAPE_SPACE_OVER_FILEMARK);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTFSFM:
+			if (!tape->capabilities.sprev)
+				return (-EIO);
+			retval=idetape_mtioctop (drive,MTFSF,mt_count-count);
+			if (retval) return (retval);
+			return (idetape_mtioctop (drive,MTBSF,1));
+		case MTBSF:
+			if (!tape->capabilities.sprev)
+				return (-EIO);
+			idetape_create_space_cmd (&pc,-(mt_count+count),IDETAPE_SPACE_OVER_FILEMARK);
+			return (idetape_queue_pc_tail (drive,&pc));
+		case MTBSFM:
+			if (!tape->capabilities.sprev)
+				return (-EIO);
+			retval=idetape_mtioctop (drive,MTBSF,mt_count+count);
+			if (retval) return (retval);
+			return (idetape_mtioctop (drive,MTFSF,1));
 		default:
 			printk ("ide-tape: MTIO operation %d not supported\n",mt_op);
 			return (-EIO);
@@ -3495,11 +4056,11 @@
 	unsigned long flags;
 	unsigned int minor;
 			
-	save_flags (flags);cli();
+	save_flags (flags);cli ();
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_open\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 
 	drive=idetape_chrdev.drive;
@@ -3527,8 +4088,6 @@
 		}
 	}
 
-	tape->last_dt_was_write=0;
-
 	return (0);
 }
 
@@ -3546,39 +4105,34 @@
 	idetape_packet_command_t pc;
 	unsigned long flags;
 			
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_chrdev_release\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	drive=idetape_chrdev.drive;
 	tape=&(drive->tape);
 	minor=MINOR (inode->i_rdev);
 
-	idetape_empty_pipeline (drive);
-
-	if (tape->last_dt_was_write) {
+	if (tape->chrdev_direction == idetape_direction_write) {
+		idetape_empty_write_pipeline (drive);
+		tape->chrdev_direction = idetape_direction_none;
 		idetape_create_write_filemark_cmd (&pc,1);	/* Write a filemark */
 		if (idetape_queue_pc_tail (drive,&pc)) {
 			printk ("ide-tape: Couldn't write a filemark\n");
 			/* ??? */
 		}
 	}
-	else {
-		idetape_create_write_filemark_cmd (&pc,0);	/* Flush buffers */
-		if (idetape_queue_pc_tail (drive,&pc)) {
-			printk ("ide-tape: Couldn't flush buffers\n");
-			/* ??? */
-		}
-	}
 
 	if (minor < 128) {
+		if (tape->chrdev_direction == idetape_direction_read)
+			idetape_empty_read_pipeline (drive);
 		if (idetape_rewind_tape (drive)) {
 			printk ("ide-tape: Rewinding tape failed\n");
 			/* ??? */
 		}
 	}
 
-	save_flags (flags);cli();
+	save_flags (flags);cli ();
 	tape->busy=0;
 	restore_flags (flags);
 
@@ -3623,13 +4177,9 @@
 {
 	int retval;
 	idetape_packet_command_t pc;
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_rewind_tape\n");
-#endif /* IDETAPE_DEBUG */	
-
-	idetape_create_write_filemark_cmd (&pc,0);	/* Flush buffers */
-	retval=idetape_queue_pc_tail (drive,&pc);
-	if (retval) return (retval);
+#endif /* IDETAPE_DEBUG_LOG */	
 	
 	idetape_create_rewind_cmd (&pc);
 	retval=idetape_queue_pc_tail (drive,&pc);
@@ -3642,6 +4192,15 @@
 	return (idetape_queue_pc_tail (drive,&pc));
 }
 
+int idetape_flush_tape_buffers (ide_drive_t *drive)
+
+{
+	idetape_packet_command_t pc;
+
+	idetape_create_write_filemark_cmd (&pc,0);
+	return (idetape_queue_pc_tail (drive,&pc));
+}
+
 /*
  *	Pipeline related functions
  */
@@ -3668,12 +4227,11 @@
 	idetape_buffer_head_t *prev_bh,*bh;
 	int buffers_num,i;
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_kmalloc_stage\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
-	if (tape->current_number_of_stages==tape->max_number_of_stages) {
-		tape->pipeline_was_full_once=1;
+	if (tape->current_number_of_stages>=tape->max_number_of_stages) {
 		return (NULL);
 	}
 		
@@ -3731,9 +4289,9 @@
 	if (stage == NULL)
 		return;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_kfree_stage\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 	
 	bh=stage->bh;
 	
@@ -3760,14 +4318,26 @@
 	idetape_buffer_head_t *bh;
 	char *ptr;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_copy_buffer_from_stage\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+	if (buffer == NULL) {
+		printk ("ide-tape: bug: buffer is null in copy_buffer_from_stage\n");
+		return;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
 	
 	ptr=buffer;
 	bh=stage->bh;
 	
 	while (bh != NULL) {
+#if IDETAPE_DEBUG_BUGS
+		if (bh->data == NULL) {
+			printk ("ide-tape: bug: bh->data is null\n");
+			return;
+		}
+#endif /* IDETAPE_DEBUG_BUGS */
 		memcpy (ptr,bh->data,IDETAPE_ALLOCATION_BLOCK);
 		bh=bh->next;
 		ptr=ptr+IDETAPE_ALLOCATION_BLOCK;
@@ -3786,14 +4356,26 @@
 	idetape_buffer_head_t *bh;
 	char *ptr;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_copy_buffer_to_stage\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+	if (buffer == NULL) {
+		printk ("ide-tape: bug: buffer is null in copy_buffer_to_stage\n");
+		return;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
 
 	ptr=buffer;
 	bh=stage->bh;
 	
 	while (bh != NULL) {
+#if IDETAPE_DEBUG_BUGS
+		if (bh->data == NULL) {
+			printk ("ide-tape: bug: bh->data is null\n");
+			return;
+		}
+#endif /* IDETAPE_DEBUG_BUGS */
 		memcpy (bh->data,ptr,IDETAPE_ALLOCATION_BLOCK);
 		bh=bh->next;
 		ptr=ptr+IDETAPE_ALLOCATION_BLOCK;
@@ -3814,12 +4396,9 @@
 {
 	idetape_tape_t *tape=&(drive->tape);
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_increase_max_pipeline_stages\n");
-#endif /* IDETAPE_DEBUG */
-
-	if (!tape->pipeline_was_full_once)
-		return;
+#endif /* IDETAPE_DEBUG_LOG */
 
 	tape->max_number_of_stages+=IDETAPE_INCREASE_STAGES_RATE*
 					(IDETAPE_MAX_PIPELINE_STAGES-IDETAPE_MIN_PIPELINE_STAGES);
@@ -3827,72 +4406,83 @@
 	if (tape->max_number_of_stages >= IDETAPE_MAX_PIPELINE_STAGES)
 		tape->max_number_of_stages = IDETAPE_MAX_PIPELINE_STAGES;
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Maximum number of stages: %d\n",tape->max_number_of_stages);
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	return;
 }
 
 /*
  *	idetape_add_stage_tail adds a new stage at the end of the pipeline.
+ *
+ *	Caller should disable interrupts, if necessary.
  */
  
 void idetape_add_stage_tail (ide_drive_t *drive,idetape_pipeline_stage_t *stage)
 
 {
 	idetape_tape_t *tape=&(drive->tape);
-	unsigned long flags;
 	
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 		printk ("Reached idetape_add_stage_tail\n");
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_LOG */
 
 	stage->next=NULL;
-	
-#if IDETAPE_DEBUG
-		printk ("Adding to the tail of the pipeline\n");
-#endif /* IDETAPE_DEBUG */
-
-	/*
-	 *	Avoid a race condition - pipeline_locked is set whenever
-	 *	we modify active_data_request from the lower level of
-	 *	the driver. We wait until active_data_request is valid.
-	 */
-	 
-	while (tape->pipeline_locked);
-
-	save_flags (flags);cli ();
 	stage->prev=tape->last_stage;
-	if (tape->first_stage != NULL)
+	if (tape->last_stage != NULL)
 		tape->last_stage->next=stage;
 	else
-		tape->first_stage=stage;
+		tape->first_stage=tape->next_stage=stage;
 	tape->last_stage=stage;
+	if (tape->next_stage == NULL)
+		tape->next_stage=tape->last_stage;
 	tape->current_number_of_stages++;
+}
 
-	/*
-	 *	Check if we are currently servicing requests in the bottom
-	 *	part of the driver.
-	 *
-	 *	If not, wait for the pipeline to be full enough (75%) before
-	 *	starting to service requests, so that we will be able to
-	 *	keep up with the higher speeds of the tape.
-	 */
+/*
+ *	idetape_remove_stage_head removes tape->first_stage from the pipeline.
+ *
+ *	Again, caller should avoid race conditions.
+ */
+ 
+void idetape_remove_stage_head (ide_drive_t *drive)
 
-	if (tape->active_data_request == NULL &&
-		tape->current_number_of_stages >= 0.75*tape->max_number_of_stages) {
-		
-		restore_flags (flags);
-		idetape_insert_pipeline_into_queue (drive);		
+{
+	idetape_tape_t *tape=&(drive->tape);
+	idetape_pipeline_stage_t *stage;
+	
+#if IDETAPE_DEBUG_LOG
+		printk ("Reached idetape_remove_stage_head\n");
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+	if (tape->first_stage == NULL) {
+		printk ("ide-tape: bug: tape->first_stage is NULL\n");
+		return;		
+	}
+	if (tape->active_stage == tape->first_stage) {
+		printk ("ide-tape: bug: Trying to free our active pipeline stage\n");
+		return;
+	}
+#endif /* IDETAPE_DEBUG_BUGS */
+	stage=tape->first_stage;
+	tape->first_stage=stage->next;
+	idetape_kfree_stage (stage);
+	tape->current_number_of_stages--;
+	if (tape->first_stage == NULL) {
+		tape->last_stage=NULL;
+#if IDETAPE_DEBUG_BUGS
+		if (tape->next_stage != NULL)
+			printk ("ide-tape: bug: tape->next_stage != NULL\n");
+		if (tape->current_number_of_stages)
+			printk ("ide-tape: bug: current_number_of_stages should be 0 now\n");
+#endif /* IDETAPE_DEBUG_BUGS */
 	}
-	else
-		restore_flags (flags);
 }
 
 /*
  *	idetape_insert_pipeline_into_queue is used to start servicing the
- *	pipeline stages.
+ *	pipeline stages, starting from tape->next_stage.
  */
  
 void idetape_insert_pipeline_into_queue (ide_drive_t *drive)
@@ -3900,55 +4490,41 @@
 {
 	idetape_tape_t *tape=&(drive->tape);
 
-	if (tape->first_stage == NULL)
+	if (tape->next_stage == NULL)
 		return;
 
 	if (tape->active_data_request == NULL) {
 		idetape_active_next_stage (drive);
-#if IDETAPE_DEBUG
-		printk ("Using ide_end\n");
-#endif /* IDETAPE_DEBUG */
 		(void) (ide_do_drive_cmd (drive,tape->active_data_request,ide_end));
 		return;
 	}
 }
 
 /*
- *	idetape_active_next_stage will "move the pipeline" one stage -
- *	Inserting the first stage into the "active place", and using kfree
- *	to free the stage.
+ *	idetape_active_next_stage will declare the next stage as "active".
  */
  
 void idetape_active_next_stage (ide_drive_t *drive)
 
 {
 	idetape_tape_t *tape=&(drive->tape);
-	idetape_pipeline_stage_t *stage;
-	struct request *rq;
-	unsigned long flags;
+	idetape_pipeline_stage_t *stage=tape->next_stage;
+	struct request *rq=&(stage->rq);
 
-#if IDETAPE_DEBUG
+#if IDETAPE_DEBUG_LOG
 	printk ("Reached idetape_active_next_stage\n");
-	if (tape->first_stage == NULL) {
-		printk ("ide-tape: ide-tape.c bug - tape->first_stage is NULL\n");
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+	if (stage == NULL) {
+		printk ("ide-tape: bug: Trying to activate a non existing stage\n");
 		return;
 	}
-#endif /* IDETAPE_DEBUG */
+#endif /* IDETAPE_DEBUG_BUGS */	
+	if (rq->cmd == IDETAPE_WRITE_REQUEST)
+		idetape_copy_buffer_from_stage (stage,tape->data_buffer);
 	
-	stage=tape->first_stage;
-
-	idetape_copy_buffer_from_stage (stage,tape->data_buffer);
-	rq=idetape_next_rq_storage (drive);
-	*rq=stage->rq;
 	rq->buffer=tape->data_buffer;
-
-	save_flags (flags);cli ();
-	tape->first_stage=stage->next;
-	if (tape->first_stage == NULL)
-		tape->last_stage=NULL;
-	tape->current_number_of_stages--;
 	tape->active_data_request=rq;
-	restore_flags (flags);
-	
-	idetape_kfree_stage (stage);
+	tape->active_stage=stage;
+	tape->next_stage=stage->next;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this