patch-1.3.48 linux/drivers/block/ide-tape.c
Next file: linux/drivers/block/ide-tape.h
Previous file: linux/drivers/block/genhd.c
Back to the patch index
Back to the overall index
- Lines: 1666
- Date:
Sun Dec 17 11:17:19 1995
- Orig file:
v1.3.47/linux/drivers/block/ide-tape.c
- Orig date:
Mon Dec 11 15:42:01 1995
diff -u --recursive --new-file v1.3.47/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/block/ide-tape.c Version 1.0 - ALPHA Dec 3, 1995
+ * linux/drivers/block/ide-tape.c Version 1.1 - ALPHA Dec 14, 1995
*
* Copyright (C) 1995 Gadi Oxman <tgud@tochnapc2.technion.ac.il>
*
@@ -17,14 +17,15 @@
* This driver is a part of the Linux ide driver and works in co-operation
* with linux/drivers/block/ide.c.
*
- * This driver provides both a block device and a character device interface to
- * the tape. The driver, in co-operation with ide.c, basically traverses the
- * request-list for the block device interface. The character device interface,
- * on the other hand, creates new requests, adds them to the request-list
- * of the block device, and waits for their completion.
+ * The driver, in co-operation with ide.c, basically traverses the
+ * request-list for the block device interface. The character device
+ * 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.
*
* The block device major and minor numbers are determined from the
- * tape relative position in the ide interfaces, as explained in ide.c.
+ * tape's relative position in the ide interfaces, as explained in ide.c.
*
* The character device interface consists of two devices:
*
@@ -34,19 +35,15 @@
* Run /usr/src/linux/drivers/block/MAKEDEV.ide to create the above entries.
* We currently support only one ide tape drive.
*
- * Although we do support requests which originate from the buffer cache to
- * some extent, it is recommended to use the character device interface when
- * performing a long read or write operation (relative to the amount of free
- * memory in your system). Otherwise, free memory will be used to cache tape
- * blocks, those cached blocks won't be used, Linux's responsiveness will
- * suffer as we start to swap.
- *
* The general magnetic tape commands compatible interface, as defined by
* include/linux/mtio.h, is accessible through the character device.
- * Our own ide-tape ioctl's can can be issued to either the block device or
- * the character device.
*
- * Opening the block device interface will be refused by default.
+ * General ide driver configuration options, such as the interrupt-unmask
+ * flag, can be configured by issuing an ioctl to the block device interface,
+ * as any other ide device.
+ *
+ * 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.
*
@@ -86,14 +83,14 @@
* performing one tape r/w request, a lot of requests
* from the other device can be queued and ide.c will
* service all of them after this single tape request.
- * Ver 1.0 ??? Integrated into Linux 1.3.??? development tree.
+ * Ver 1.0 Dec 11 95 Integrated into Linux 1.3.46 development tree.
* On each read / write request, we now ask the drive
* if we can transfer a constant number of bytes
* (a parameter of the drive) only to its buffers,
* without causing actual media access. If we can't,
* we just wait until we can by polling the DSC bit.
* This ensures that while we are not transferring
- * more bytes than the constant reffered to above, the
+ * more bytes than the constant referred to above, the
* interrupt latency will not become too high and
* we won't cause an interrupt timeout, as happened
* occasionally in the previous version.
@@ -118,6 +115,26 @@
* Our data transfer buffer is allocated on startup,
* rather than before each data transfer. This should
* ensure that we will indeed have a data buffer.
+ * Ver 1.1 Dec 14 95 Fixed random problems which occured when the tape
+ * shared an interface with another device.
+ * (poll_for_dsc was a complete mess).
+ * Removed some old (non-active) code which had
+ * to do with supporting buffer cache originated
+ * requests.
+ * The block device interface can now be opened, so
+ * that general ide driver features like the unmask
+ * interrupts flag can be selected with an ioctl.
+ * This is the only use of the block device interface.
+ * New fast pipelined operation mode (currently only on
+ * writes). When using the pipelined mode, the
+ * throughput can potentially reach the maximum
+ * tape supported throughput, regardless of the
+ * user backup program. On my tape drive, it sometimes
+ * boosted performance by a factor of 2. Pipelined
+ * mode is enabled by default, but since it has a few
+ * downfalls as well, you may want to disable it.
+ * A short explanation of the pipelined operation mode
+ * is available below.
*
* We are currently in an *alpha* stage. The driver is not complete and not
* much tested. I would strongly suggest to:
@@ -135,6 +152,72 @@
*
*/
+/*
+ * A short explanation of the pipelined operation mode.
+ *
+ * Pipelined mode is currently only implemented on writes. Reads are still
+ * performed in the slow non-pipelined mode.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * Concerning (1):
+ *
+ * 1. We allocate stages dynamically only when we need them. When
+ * we don't need them, we don't consume additional memory. In
+ * case we can't allocate stages, we just manage without them
+ * (at the expense of decreased throughput) so when Linux is
+ * tight in memory, we will not pose additional difficulties.
+ *
+ * 2. The maximum number of stages (which is, in fact, the maximum
+ * amount of memory) which we allocate is limited by the compile
+ * time parameter IDETAPE_MAX_PIPELINE_STAGES.
+ *
+ * 3. The maximum number of stages is a controlled parameter - We
+ * don't start from the user defined maximum number of stages
+ * but from the lower IDETAPE_MIN_PIPELINE_STAGES (again, we
+ * will not even allocate this amount of stages if the user
+ * program can't handle the speed). We then implement a feedback
+ * loop which checks if the pipeline is empty, and if it is, we
+ * increase the maximum number of stages as necessary until we
+ * reach the optimum value which just manages to keep the tape
+ * busy with with minimum allocated memory or until we reach
+ * IDETAPE_MAX_PIPELINE_STAGES.
+ *
+ * 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
+ * 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.
+ *
+ * You can enable/disable/tune the pipelined operation mode by adjusting
+ * the compile time parameters in ide-tape.h.
+ */
+
#include <linux/hdreg.h>
#include <linux/types.h>
#include <linux/string.h>
@@ -161,7 +244,7 @@
/*
* Main Linux ide driver include file
*
- * Automatically includes our first include file - ide-tape1.h.
+ * Automatically includes our include file - ide-tape.h.
*/
#include "ide.h"
@@ -214,6 +297,8 @@
*
*/
+#define IDETAPE_FIRST_REQUEST 90
+
/*
* IDETAPE_PACKET_COMMAND_REQUEST_TYPE1 is used to queue a packet command
* in the request queue. We will wait for DSC before issuing the command
@@ -242,6 +327,15 @@
#define IDETAPE_READ_REQUEST 92
#define IDETAPE_WRITE_REQUEST 93
+#define IDETAPE_LAST_REQUEST 93
+
+/*
+ * A macro which can be used to check if a we support a given
+ * request command.
+ */
+
+#define IDETAPE_REQUEST_CMD(cmd) ((cmd >= IDETAPE_FIRST_REQUEST) && (cmd <= IDETAPE_LAST_REQUEST))
+
/*
* We are now able to postpone an idetape request in the stage
* where it is polling for DSC and service requests from the other
@@ -629,7 +723,7 @@
void idetape_postpone_request (ide_drive_t *drive);
void idetape_poll_for_dsc (unsigned long data);
-void idetape_put_back_postponed_request (ide_drive_t *drive);
+void idetape_poll_for_dsc_direct (unsigned long data);
void idetape_media_access_finished (ide_drive_t *drive);
/*
@@ -695,10 +789,29 @@
*/
int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count);
+
+/*
+ * 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);
+
+/*
+ * idetape_queue_rw_tail will add a command to the tail of the device
+ * request queue and wait for it to finish. This is used when we
+ * can not allocate pipeline stages (or in non-pipelined mode).
+ */
+
int idetape_queue_rw_tail (ide_drive_t *drive,int cmd,int blocks,char *buffer);
+
+/*
+ * Adds a packet command request to the tail of the device request
+ * queue and waits for it to be serviced.
+ */
+
int idetape_queue_pc_tail (ide_drive_t *drive,idetape_packet_command_t *pc);
-void idetape_fake_read (ide_drive_t *drive);
int idetape_position_tape (ide_drive_t *drive,unsigned long block);
int idetape_rewind_tape (ide_drive_t *drive);
@@ -712,11 +825,24 @@
* General utility functions
*/
-void idetape_fixstring (byte *s, const int bytecount, const int byteswap);
unsigned long idetape_swap_long (unsigned long temp);
unsigned short idetape_swap_short (unsigned short temp);
/*
+ * Pipeline related functions
+ */
+
+idetape_pipeline_stage_t *idetape_kmalloc_stage (ide_drive_t *drive);
+void idetape_kfree_stage (idetape_pipeline_stage_t *stage);
+void idetape_copy_buffer_from_stage (idetape_pipeline_stage_t *stage,char *buffer);
+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_active_next_stage (ide_drive_t *drive);
+void idetape_empty_pipeline (ide_drive_t *drive);
+void idetape_insert_pipeline_into_queue (ide_drive_t *drive);
+
+/*
* For general magnetic tape device compatibility.
*/
@@ -972,37 +1098,62 @@
void idetape_setup (ide_drive_t *drive)
{
- int buffer_size;
idetape_tape_t *tape=&(drive->tape);
-
+ unsigned int allocation_length;
+
#if IDETAPE_DEBUG
printk ("ide-tape: Reached idetape_setup\n");
#endif /* IDETAPE_DEBUG */
drive->ready_stat = 0; /* With an ATAPI device, we can issue packet commands */
/* regardless of the state of DRDY */
+ HWIF(drive)->tape_drive=drive;
+
tape->block_address=0;
tape->block_address_valid=0;
- tape->locate_to=0;
- tape->locate_retries=0;
tape->pc_stack_index=0;
tape->failed_pc=NULL;
tape->postponed_rq=NULL;
- tape->last_written_valid=0;
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->error_in_pipeline_stage=0;
+ tape->pipeline_locked=0;
+
+ tape->request_status=0;
+ tape->request_dsc_callback=0;
+
+#if IDETAPE_PIPELINE
+ tape->max_number_of_stages=IDETAPE_MIN_PIPELINE_STAGES;
+ printk ("ide-tape: Operating in pipelined (fast and tricky) write mode.\n");
+#else
+ tape->max_number_of_stages=0;
+ printk ("ide-tape: Operating in non-pipelined (slow and safe) write mode.\n");
+#endif /* IDETAPE_PIPELINE */
+ printk ("ide-tape: Operating in non-pipelined (slow and safe) read mode.\n");
+
idetape_get_mode_sense_results (drive);
- buffer_size=tape->capabilities.ctl*tape->tape_block_size;
- tape->data_buffer=kmalloc (buffer_size,GFP_KERNEL);
- if (tape->data_buffer == NULL) {
- printk ("ide-tape: FATAL - Can not allocate %d bytes for data transfer buffer\n",buffer_size);
+ tape->data_buffer_size=tape->capabilities.ctl*tape->tape_block_size;
+
+ allocation_length=tape->data_buffer_size;
+ if (tape->data_buffer_size % IDETAPE_ALLOCATION_BLOCK)
+ allocation_length+=IDETAPE_ALLOCATION_BLOCK;
+
+ tape->data_buffer=kmalloc (allocation_length,GFP_KERNEL);
+ tape->temp_data_buffer=kmalloc (allocation_length,GFP_KERNEL);
+ if (tape->data_buffer == NULL || tape->temp_data_buffer == NULL) {
+ printk ("ide-tape: FATAL - Can not allocate 2 buffers of %d bytes each\n",allocation_length);
printk ("ide-tape: Aborting character device installation\n");
idetape_drive_already_found=0;
unregister_chrdev (idetape_chrdev.major,idetape_chrdev.name);
return;
}
- printk ("ide-tape: Speed - %d KBps. Recommended transfer unit - %d bytes.\n",tape->capabilities.speed,buffer_size);
+
+ printk ("ide-tape: Tape speed - %d KBps. Recommended transfer unit - %d bytes.\n",tape->capabilities.speed,tape->data_buffer_size);
+
return;
}
@@ -1129,7 +1280,7 @@
tape=&(drive->tape);
-#ifdef IDETAPE_DEBUG
+#if IDETAPE_DEBUG
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 */
@@ -1262,7 +1413,7 @@
if (pc->callback==NULL)
printk ("ide-tape: ide-tape bug - Callback function not set !\n");
else
-#endif IDETAPE_DEBUG
+#endif /* IDETAPE_DEBUG */
(*pc->callback)(drive); /* Command finished - Call the callback function */
return;
}
@@ -1334,16 +1485,19 @@
idetape_tape_t *tape;
unsigned long flags;
struct request *rq;
-
+ idetape_status_reg_t status;
+
tape=&(drive->tape);
+ status.all=IN_BYTE (IDETAPE_STATUS_REG);
+ sti ();
+
#if IDETAPE_DEBUG
printk ("Reached idetape_postpone_request\n");
if (tape->postponed_rq != NULL)
printk ("ide-tape.c bug - postponed_rq not NULL in idetape_postpone_request\n");
#endif /* IDETAPE_DEBUG */
- tape->dsc_count=0;
tape->dsc_timer.expires=jiffies + tape->dsc_polling_frequency; /* Set timer to poll for */
tape->dsc_timeout=jiffies+IDETAPE_DSC_TIMEOUT; /* actual completion */
tape->dsc_timer.data=(unsigned long) drive;
@@ -1353,61 +1507,55 @@
/*
* Remove current request from the request queue:
*/
- save_flags(flags); /* Let ide.c handle another request */
- cli();
+
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->dsc_polling_start=jiffies;
+
+ tape->request_status=0;
+ tape->request_dsc_callback=0;
+ tape->last_status=status.all;
+
+ tape->dsc_polling_start=jiffies;
add_timer(&(tape->dsc_timer)); /* Activate the polling timer */
}
-
/*
- * idetape_poll_for_dsc gets invoked by a timer (which was set
- * by idetape_postpone_request) to poll for the DSC bit
- * in the status register. When the DSC bit is set, or a timeout is
- * reached, we put back the postponed request in front of the request
- * queue.
+ * idetape_poll_for_dsc_direct is called from idetape_poll_for_dsc
+ * to handle the case in which we can safely communicate with the tape
+ * (since no other request for this hwgroup is active).
*/
-void idetape_poll_for_dsc (unsigned long data)
+void idetape_poll_for_dsc_direct (unsigned long data)
{
- ide_drive_t *drive;
- idetape_tape_t *tape;
-
+ 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;
- idetape_packet_command_t *pc;
+ unsigned long flags;
- drive=(ide_drive_t *) data;
- tape=&(drive->tape);
- pc=tape->pc;
-
#if IDETAPE_DEBUG
- printk ("%s: idetape_poll_for_dsc called\n",drive->name);
+ printk ("%s: idetape_poll_for_dsc_direct called\n",drive->name);
#endif /* IDETAPE_DEBUG */
+ 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)
- tape->dsc_count++;
- else {
- if (tape->dsc_count)
- printk ("ide-tape: DSC fluctuation detected - Restarting DSC count\n");
- tape->dsc_count=0;
- }
-
- if (tape->dsc_count == IDETAPE_DSC_COUNT) { /* DSC received */
+ if (status.b.dsc) { /* DSC received */
tape->dsc_received=1;
del_timer (&(tape->dsc_timer)); /* Stop polling and put back the postponed */
idetape_put_back_postponed_request (drive); /* request in the request queue */
return;
}
-
+
if (jiffies > tape->dsc_timeout) { /* Timeout */
tape->dsc_received=0;
del_timer (&(tape->dsc_timer));
@@ -1415,23 +1563,129 @@
idetape_put_back_postponed_request (drive);
return;
}
-
+
/* Poll again */
+
+ if (jiffies - tape->dsc_polling_start > IDETAPE_FAST_SLOW_THRESHOLD)
+ tape->dsc_timer.expires = jiffies + IDETAPE_DSC_SLOW_MEDIA_ACCESS_FREQUENCY;
+ else
+ tape->dsc_timer.expires = jiffies + tape->dsc_polling_frequency;
+ add_timer(&(tape->dsc_timer));
+ return;
+}
+
+/*
+ * idetape_poll_for_dsc gets invoked by a timer (which was set
+ * 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 ...
+ */
+
+void idetape_poll_for_dsc (unsigned long data)
+
+{
+ ide_drive_t *drive=(ide_drive_t *) data;
+ unsigned int major = HWIF(drive)->major;
+ 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
+ printk ("%s: idetape_poll_for_dsc called\n",drive->name);
+#endif /* IDETAPE_DEBUG */
+
+ /*
+ * Check if the other device is idle. If there are no requests,
+ * we can safely access the tape.
+ */
+ if (bdev->current_request == NULL) {
+ restore_flags (flags);
+ 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.
+ */
+ restore_flags (flags);
+ tape->dsc_timer.expires = jiffies + tape->dsc_polling_frequency/2;
+ add_timer(&(tape->dsc_timer));
+ return;
+ }
+
+ /*
+ * We now know that:
+ *
+ * 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 will therefor ask ide.c to perform the tasks on our behalf.
+ */
+
+ 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);
+ return;
+ }
+
+ if (jiffies > tape->dsc_timeout) { /* Timeout */
+ tape->dsc_received=1;
+ 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.
+ */
+
+ tape->request_status=1;
+ restore_flags (flags);
+
+ /* Poll again */
+
if (jiffies - tape->dsc_polling_start > IDETAPE_FAST_SLOW_THRESHOLD)
tape->dsc_timer.expires = jiffies + IDETAPE_DSC_SLOW_MEDIA_ACCESS_FREQUENCY;
else
tape->dsc_timer.expires = jiffies + tape->dsc_polling_frequency;
-/* init_timer (&(tape->dsc_timer)); */
add_timer(&(tape->dsc_timer));
return;
}
/*
- * idetape_put_back_postponed_request gets called by
- * idetape_poll_for_dsc when we decided to stop polling - Either
- * becase we received DSC or because we decided to give up and
- * stop waiting.
+ * 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.
*/
void idetape_put_back_postponed_request (ide_drive_t *drive)
@@ -1571,7 +1825,7 @@
#if IDETAPE_DEBUG
printk ("ide-tape: Reached idetape_read_callback\n");
#endif /* IDETAPE_DEBUG */
- tape->block_address+=tape->pc->actually_transferred/512;
+ tape->block_address+=tape->pc->actually_transferred/tape->tape_block_size;
if (!tape->pc->error) {
#if IDETAPE_DEBUG
printk ("Request completed\n");
@@ -1588,33 +1842,6 @@
return;
}
-void idetape_fake_read (ide_drive_t *drive)
-
-{
- idetape_tape_t *tape;
- struct request *rq;
- unsigned long i;
-
- tape=&(drive->tape);
- rq=HWGROUP(drive)->rq;
-#if IDETAPE_DEBUG
- printk ("ide-tape: Reached idetape_fake_read\n");
-#endif /* IDETAPE_DEBUG */
-
-#if IDETAPE_DEBUG
- printk ("Request completed\n");
-#endif /* IDETAPE_DEBUG */
-
- for (i=0;i<rq->current_nr_sectors*512;i++)
- rq->buffer [i]=0;
-
- tape->block_address+=rq->current_nr_sectors;
- rq->sector+=rq->current_nr_sectors;
- rq->nr_sectors-=rq->current_nr_sectors;
- rq->current_nr_sectors=0;
- idetape_end_request (1,HWGROUP (drive));
-}
-
void idetape_write_callback (ide_drive_t *drive)
{
@@ -1626,7 +1853,7 @@
#if IDETAPE_DEBUG
printk ("ide-tape: Reached idetape_write_callback\n");
#endif /* IDETAPE_DEBUG */
- tape->block_address+=tape->pc->actually_transferred/512;
+ tape->block_address+=tape->pc->actually_transferred/tape->tape_block_size;
if (!tape->pc->error) {
#if IDETAPE_DEBUG
printk ("Request completed\n");
@@ -1743,9 +1970,9 @@
idetape_inquiry_result_t *result;
result=(idetape_inquiry_result_t *) buffer;
- idetape_fixstring (result->vendor_id,8,0);
- idetape_fixstring (result->product_id,16,0);
- idetape_fixstring (result->revision_level,4,0);
+ ide_fixstring (result->vendor_id,8,0);
+ ide_fixstring (result->product_id,16,0);
+ ide_fixstring (result->revision_level,4,0);
if (result->response_format != 2) {
printk ("The INQUIRY Data Format is unknown to us !\n");
@@ -2258,26 +2485,54 @@
{
ide_drive_t *drive = hwgroup->drive;
struct request *rq = hwgroup->rq;
-
- if (rq->cmd == READ || rq->cmd == WRITE) { /* Buffer cache originated request */
- ide_end_request (uptodate,hwgroup); /* Let the common code handle it */
- return;
- }
+ idetape_tape_t *tape = &(drive->tape);
+
/* Our own originated request */
rq->errors=!uptodate; /* rq->errors will tell us if the request was successfull */
- ide_end_drive_cmd (drive, 0, 0);
+
+ 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;
- /* The "up(rq->sem);" does the necessary "wake_up()" for us,
- * providing we started sleeping with a "down()" call.
- * This may not be the case if the driver converts a READ or WRITE
- * request into a special internal rq->cmd type. -ml
- */
-
- /*
- * As Mark explained, we do not need a "wake_up()" call here,
- * since we are always sleeping with a "down()" call.
- */
+ /*
+ * 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;
+ idetape_increase_max_pipeline_stages (drive);
+ 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.
+ */
+
+ ide_do_drive_cmd (drive,tape->active_data_request,ide_end);
+ }
+ ide_end_drive_cmd (drive, 0, 0);
}
/*
@@ -2287,19 +2542,33 @@
void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
{
- idetape_tape_t *tape;
+ idetape_tape_t *tape=&(drive->tape);
idetape_packet_command_t *pc;
- struct request *new_rq;
idetape_status_reg_t status;
- tape=&(drive->tape);
-
#if IDETAPE_DEBUG
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 */
+ if (!IDETAPE_REQUEST_CMD (rq->cmd)) {
+
+ /*
+ * We do not support buffer cache originated requests.
+ */
+
+ printk ("ide-tape: Unsupported command in request queue\n");
+ printk ("ide-tape: The block device interface should not be used for data transfers.\n");
+ printk ("ide-tape: Use the character device interfaces\n");
+ printk ("ide-tape: /dev/ht0 and /dev/nht0 instead.\n");
+ printk ("ide-tape: (Run linux/drivers/block/MAKEDEV.ide to create them)\n");
+ printk ("ide-tape: Aborting request.\n");
+
+ ide_end_request (0,HWGROUP (drive)); /* Let the common code handle it */
+ return;
+ }
+
/* Retry a failed packet command */
if (tape->failed_pc != NULL && tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
@@ -2310,13 +2579,13 @@
/* Check if we have a postponed request */
if (tape->postponed_rq != NULL) {
-/* #if IDETAPE_DEBUG */
+ #if IDETAPE_DEBUG
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 */
if (rq->cmd == IDETAPE_PACKET_COMMAND_REQUEST_TYPE1) {
/* Media access command */
@@ -2335,69 +2604,10 @@
tape->postponed_rq = NULL;
}
-
- if (rq->cmd == READ || rq->cmd == IDETAPE_READ_REQUEST || rq->cmd == WRITE || rq->cmd == IDETAPE_WRITE_REQUEST) {
-
- if (!tape->block_address_valid || tape->block_address!=rq->sector) { /* Re-position the tape */
-
- if (tape->locate_to == rq->sector && tape->locate_retries > IDETAPE_LOCATE_RETRIES) {
- printk ("ide-tape: Can not reach block %lu - Aborting request\n",rq->sector);
- tape->locate_retries=0;
- idetape_end_request (0,HWGROUP (drive));
- return;
- }
-
- if (tape->locate_to == rq->sector)
- tape->locate_retries++;
- else {
- tape->locate_to=rq->sector;
- tape->locate_retries=1;
- }
-#if IDETAPE_DEBUG
- printk ("ide-tape: We are not at the requested block\n");
- printk ("ide-tape: Re-positioning tape\n");
- printk ("ide-tape: Adding READ POSITION command to the head of the queue\n");
-#endif /* IDETAPE_DEBUG */
- pc=idetape_next_pc_storage (drive);
- new_rq=idetape_next_rq_storage (drive);
- idetape_create_read_position_cmd (pc);
- pc->buffer=pc->temp_buffer;
- pc->buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
- pc->current_position=pc->temp_buffer;
- idetape_queue_pc_head (drive,pc,new_rq);
-#if IDETAPE_DEBUG
- printk ("ide-tape: Adding LOCATE %lu command to the head of the queue\n",rq->sector);
-#endif /* IDETAPE_DEBUG */
- pc=idetape_next_pc_storage (drive);
- new_rq=idetape_next_rq_storage (drive);
- idetape_create_locate_cmd (pc,rq->sector,0);
- idetape_queue_pc_head (drive,pc,new_rq);
-
- if (!tape->block_address_valid) { /* The tape doesn't know the position - help it */
- /* by rewinding the tape */
-#if IDETAPE_DEBUG
- printk ("ide-tape: Adding LOCATE 0 command to the head of the queue\n");
-#endif /* IDETAPE_DEBUG */
- pc=idetape_next_pc_storage (drive);
- new_rq=idetape_next_rq_storage (drive);
- idetape_create_locate_cmd (pc,0,0);
- idetape_queue_pc_head (drive,pc,new_rq);
- }
-
- return;
- }
- else
- tape->locate_retries=0;
- }
-
switch (rq->cmd) {
- case READ:
case IDETAPE_READ_REQUEST:
#if IDETAPE_DEBUG
- if (rq->cmd == READ)
- printk ("ide-tape: Handling buffer cache READ request\n");
- else
- printk ("ide-tape: Handling our own (not buffer cache originated) READ request\n");
+ 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 */
@@ -2409,27 +2619,21 @@
return;
}
- tape->last_written_valid=0;
-
pc=idetape_next_pc_storage (drive);
idetape_create_read_cmd (pc,rq->current_nr_sectors);
pc->buffer=rq->buffer;
- pc->buffer_size=rq->current_nr_sectors*512;
+ pc->buffer_size=rq->current_nr_sectors*tape->tape_block_size;
pc->current_position=rq->buffer;
- pc->request_transfer=rq->current_nr_sectors*512;
+ pc->request_transfer=rq->current_nr_sectors*tape->tape_block_size;
idetape_issue_packet_command (drive,pc,&idetape_pc_intr);
return;
- case WRITE:
case IDETAPE_WRITE_REQUEST:
#if IDETAPE_DEBUG
- if (rq->cmd == WRITE)
- printk ("ide-tape: Handling buffer cache WRITE request\n");
- else
- printk ("ide-tape: Handling our own (not buffer cache originated) WRITE request\n");
+ printk ("ide-tape: Handling our own (not buffer cache originated) WRITE request\n");
#endif /* IDETAPE_DEBUG */
status.all=IN_BYTE (IDETAPE_STATUS_REG);
@@ -2442,17 +2646,14 @@
return;
}
- tape->last_written_valid=1;
- tape->last_written_block=rq->sector;
-
pc=idetape_next_pc_storage (drive);
idetape_create_write_cmd (pc,rq->current_nr_sectors);
pc->buffer=rq->buffer;
- pc->buffer_size=rq->current_nr_sectors*512;
+ pc->buffer_size=rq->current_nr_sectors*tape->tape_block_size;
pc->current_position=rq->buffer;
- pc->request_transfer=rq->current_nr_sectors*512;
+ pc->request_transfer=rq->current_nr_sectors*tape->tape_block_size;
idetape_issue_packet_command (drive,pc,&idetape_pc_intr);
return;
@@ -2468,7 +2669,7 @@
if (!status.b.dsc) { /* Tape buffers are still not ready */
#if IDETAPE_DEBUG
printk ("ide-tape: DSC != 1 - Postponing packet command request\n");
-#endif IDETAPE_DEBUG
+#endif /* IDETAPE_DEBUG */
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;
@@ -2479,10 +2680,11 @@
pc=(idetape_packet_command_t *) rq->buffer;
idetape_issue_packet_command (drive,pc,&idetape_pc_intr);
return;
-
+#if IDETAPE_DEBUG
default:
- printk ("ide-tape: Unknown command in request - Aborting request\n");
+ printk ("ide-tape: bug in IDETAPE_REQUEST_CMD macro\n");
idetape_end_request (0,HWGROUP (drive));
+#endif /* IDETAPE_DEBUG */
}
}
@@ -2546,6 +2748,19 @@
(void) ide_do_drive_cmd (drive, rq, ide_preempt);
}
+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);
+
+ down (&sem);
+}
+
/*
* idetape_queue_rw_tail is typically called from the character device
* interface to generate a read/write request for the block device interface
@@ -2573,40 +2788,133 @@
rq.sector = tape->block_address;
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);
}
-/*
- * Copied from ide.c (declared static there)
+ /*
+ * 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.
*/
-void idetape_fixstring (byte *s, const int bytecount, const int byteswap)
+int idetape_add_chrdev_write_request (ide_drive_t *drive,int cmd,int blocks,char *buffer)
+
{
- byte *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */
+ idetape_tape_t *tape = &(drive->tape);
+ idetape_pipeline_stage_t *new_stage;
+ struct request *rq;
+
+#if IDETAPE_DEBUG
+ printk ("Reached idetape_add_chrdev_write_request\n");
+ printk ("Trying to allocate stage - ");
+#endif /* IDETAPE_DEBUG */
+
+ if (tape->error_in_pipeline_stage) /* Return a deferred error */
+ return (-EIO);
+
+ new_stage=idetape_kmalloc_stage (drive);
+
+ /*
+ * If we don't have a new stage, wait for more and more requests
+ * to finish, and try to allocate after each one.
+ *
+ * Pay special attention to possible race conditions.
+ */
- if (byteswap) {
- /* convert from big-endian to host byte order */
- for (p = end ; p != s;) {
- unsigned short *pp = (unsigned short *) (p -= 2);
- *pp = ntohs(*pp);
+ 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));
+ new_stage=idetape_kmalloc_stage (drive);
}
+ else
+ break; /* Linux is short on memory */
}
-
- /* strip leading blanks */
- while (s != end && *s == ' ')
- ++s;
-
- /* compress internal blanks and strip trailing blanks */
- while (s != end && *s) {
- if (*s++ != ' ' || (s != end && *s && *s != ' '))
- *p++ = *(s-1);
+
+ /*
+ * If we don't have a new_stage, 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));
}
- /* wipe out trailing garbage */
- while (p != end)
- *p++ = '\0';
+ 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->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);
+ idetape_add_stage_tail (drive,new_stage);
+
+ return (0);
}
+void idetape_empty_pipeline (ide_drive_t *drive)
+
+{
+ idetape_tape_t *tape = &(drive->tape);
+
+ tape->pipeline_was_full_once=0;
+
+ while (tape->pipeline_locked);
+
+ if (tape->active_data_request == NULL)
+ idetape_insert_pipeline_into_queue (drive);
+
+ 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);
+
+ tape->error_in_pipeline_stage=0;
+
+ /*
+ * On the next backup, perform the feedback loop again.
+ * (I don't want to keep sense information between backups,
+ * as some systems are constantly on, and the system load
+ * can be totally different on the next backup).
+ */
+
+#if IDETAPE_PIPELINE
+ tape->max_number_of_stages=IDETAPE_MIN_PIPELINE_STAGES;
+#else
+ 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) {
+ printk ("ide-tape: ide-tape pipeline bug\n");
+ }
+#endif /* IDETAPE_DEBUG */
+}
+
+
/*
* idetape_zero_packet_command just zeros a packet command and
* sets the number of retries to 0, as we haven't retried it yet.
@@ -2722,28 +3030,48 @@
/*
* Block device interface functions
*
- * The default action is not to allow direct access to the block device
- * interface (-EBUSY will be returned on open).
+ * The block device interface should not be used for data transfers.
+ * However, we still allow opening it so that we can issue general
+ * ide driver configuration ioctl's, such as the interrupt unmask feature.
*/
int idetape_blkdev_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
{
-#if IDETAPE_ALLOW_OPENING_BLOCK_DEVICE
+ idetape_tape_t *tape=&(drive->tape);
+ unsigned long flags;
+
+ save_flags (flags);cli();
+
+#if IDETAPE_DEBUG
+ printk ("Reached idetape_blkdev_open\n");
+#endif /* IDETAPE_DEBUG */
+
+ if (tape->busy) {
+ restore_flags (flags); /* Allowing access only through one */
+ return (-EBUSY); /* one file descriptor */
+ }
+
+ tape->busy=1;
+ restore_flags (flags);
+
return (0);
-#else
- printk ("ide-tape: The block device interface should not be used.\n");
- printk ("ide-tape: Use the character device interfaces\n");
- printk ("ide-tape: /dev/ht0 and /dev/nht0 instead.\n");
- printk ("ide-tape: (Run linux/drivers/block/MAKEDEV.ide to create them)\n");
- printk ("ide-tape: Refusing open request.\n");
- return (-EBUSY);
-#endif /* IDETAPE_ALLOW_OPENING_BLOCK_DEVICE */
}
void idetape_blkdev_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
{
+ idetape_tape_t *tape=&(drive->tape);
+ unsigned long flags;
+
+#if IDETAPE_DEBUG
+ printk ("Reached idetape_blkdev_release\n");
+#endif /* IDETAPE_DEBUG */
+
+ save_flags (flags);cli();
+ tape->busy=0;
+ restore_flags (flags);
+
return;
}
@@ -2776,7 +3104,7 @@
/*
* Our character device read / write functions.
*
- * The tape is optimized to maximize throughpot when it is transfering
+ * The tape is optimized to maximize throughput when it is transfering
* an integral number of the "continous transfer limit", which is
* a parameter of the specific tape (26 KB on my particular tape). The
* resulting increase in performance should be dramatical. In the
@@ -2872,9 +3200,8 @@
if (count==0)
return (0);
-
- blocks=count/512;
- remainder=count%512;
+ blocks=count/tape->tape_block_size;
+ remainder=count%tape->tape_block_size;
if (remainder) {
#if IDETAPE_DEBUG
printk ("ide-tape: Padding read to block boundary\n");
@@ -2888,7 +3215,7 @@
retval=idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,blocks,tape->data_buffer);
if (retval) {
printk ("ide-tape: Error occured while reading\n");
- actually_read=512*(tape->block_address-previous_block_address);
+ actually_read=tape->tape_block_size*(tape->block_address-previous_block_address);
if (actually_read > count)
actually_read=count;
if (actually_read != 0)
@@ -2907,7 +3234,7 @@
{
ide_drive_t *drive;
idetape_tape_t *tape;
- int blocks,remainder,retval,ctl_bytes;
+ int blocks,remainder,retval;
const char *buf_ptr;
unsigned long previous_block_address,actually_written;
@@ -2917,6 +3244,7 @@
drive=idetape_chrdev.drive;
tape=&(drive->tape);
+
tape->last_dt_was_write=1;
if (count==0)
@@ -2924,26 +3252,25 @@
actually_written=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 ("Copying %d bytes from the user space memory\n",ctl_bytes);
-#endif /* IDETAPE_DEBUG */
- memcpy_fromfs (tape->data_buffer,buf_ptr,ctl_bytes);
- buf_ptr+=ctl_bytes;
-#if IDETAPE_DEBUG
- printk ("Adding a WRITE request to the block device request queue\n");
-#endif /* IDETAPE_DEBUG */
+ 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_queue_rw_tail (drive,IDETAPE_WRITE_REQUEST,tape->capabilities.ctl,tape->data_buffer);
- actually_written+=tape->tape_block_size*(tape->block_address-previous_block_address);
+ retval=idetape_add_chrdev_write_request (drive,IDETAPE_WRITE_REQUEST,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");
- return (actually_written);
+ if (tape->max_number_of_stages)
+ return (0);
+ else
+ return (actually_written);
}
blocks--;
}
@@ -2969,20 +3296,20 @@
drive=idetape_chrdev.drive;
tape=&(drive->tape);
- blocks=count/512;
- remainder=count%512;
+ blocks=count/tape->tape_block_size;
+ remainder=count%tape->tape_block_size;
if (remainder)
blocks++;
#if IDETAPE_DEBUG
printk ("Copying %d bytes from the user space memory\n",count);
#endif /* IDETAPE_DEBUG */
- memcpy_fromfs (tape->data_buffer,buf,count);
+ memcpy_fromfs (tape->temp_data_buffer,buf,count);
if (remainder) {
#if IDETAPE_DEBUG
printk ("ide-tape: Padding written data to block boundary\n");
#endif /* IDETAPE_DEBUG */
- ptr=tape->data_buffer+(blocks-1)*512;
+ ptr=tape->temp_data_buffer+(blocks-1)*tape->tape_block_size;
memset (ptr,0,remainder);
}
#if IDETAPE_DEBUG
@@ -2990,10 +3317,13 @@
#endif /* IDETAPE_DEBUG */
previous_block_address=tape->block_address;
- retval=idetape_queue_rw_tail (drive,IDETAPE_WRITE_REQUEST,blocks,tape->data_buffer);
+ retval=idetape_add_chrdev_write_request (drive,IDETAPE_WRITE_REQUEST,blocks,tape->temp_data_buffer);
if (retval) {
printk ("ide-tape: Error occured while writing\n");
- actually_written=512*(tape->block_address-previous_block_address);
+ if (tape->max_number_of_stages)
+ actually_written=0;
+ else
+ actually_written=tape->tape_block_size*(tape->block_address-previous_block_address);
if (actually_written > count)
actually_written=count;
return (actually_written);
@@ -3164,9 +3494,8 @@
idetape_tape_t *tape;
unsigned long flags;
unsigned int minor;
-
- save_flags (flags);
- cli();
+
+ save_flags (flags);cli();
#if IDETAPE_DEBUG
printk ("Reached idetape_chrdev_open\n");
@@ -3199,7 +3528,7 @@
}
tape->last_dt_was_write=0;
-
+
return (0);
}
@@ -3225,6 +3554,8 @@
tape=&(drive->tape);
minor=MINOR (inode->i_rdev);
+ idetape_empty_pipeline (drive);
+
if (tape->last_dt_was_write) {
idetape_create_write_filemark_cmd (&pc,1); /* Write a filemark */
if (idetape_queue_pc_tail (drive,&pc)) {
@@ -3247,11 +3578,10 @@
}
}
- save_flags (flags);
- cli();
+ save_flags (flags);cli();
tape->busy=0;
restore_flags (flags);
-
+
return;
}
@@ -3310,4 +3640,315 @@
pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
pc.current_position=pc.temp_buffer;
return (idetape_queue_pc_tail (drive,&pc));
+}
+
+/*
+ * Pipeline related functions
+ */
+
+/*
+ * idetape_kmalloc_stage uses kmalloc to allocate a pipeline stage,
+ * along with all the necessary small buffers which together make
+ * a buffer of size tape->data_buffer_size or a bit more, in case
+ * it is not a multiply of IDETAPE_ALLOCATION_BLOCK (it isn't ...).
+ *
+ * Returns a pointer to the new allocated stage, or NULL if we
+ * can't (or don't want to, in case we already have too many stages)
+ * allocate a stage.
+ *
+ * Pipeline stages are optional and are used to increase performance.
+ * If we can't allocate them, we'll manage without them.
+ */
+
+idetape_pipeline_stage_t *idetape_kmalloc_stage (ide_drive_t *drive)
+
+{
+ idetape_tape_t *tape=&(drive->tape);
+ idetape_pipeline_stage_t *new_stage;
+ idetape_buffer_head_t *prev_bh,*bh;
+ int buffers_num,i;
+
+#if IDETAPE_DEBUG
+ printk ("Reached idetape_kmalloc_stage\n");
+#endif /* IDETAPE_DEBUG */
+
+ if (tape->current_number_of_stages==tape->max_number_of_stages) {
+ tape->pipeline_was_full_once=1;
+ return (NULL);
+ }
+
+ new_stage=(idetape_pipeline_stage_t *) kmalloc (sizeof (idetape_pipeline_stage_t),GFP_KERNEL);
+ if (new_stage==NULL)
+ return (NULL);
+
+ new_stage->next=new_stage->prev=NULL;
+
+ buffers_num=tape->data_buffer_size / IDETAPE_ALLOCATION_BLOCK;
+ if (tape->data_buffer_size % IDETAPE_ALLOCATION_BLOCK)
+ buffers_num++;
+
+ prev_bh=new_stage->bh=(idetape_buffer_head_t *) kmalloc (sizeof (idetape_buffer_head_t),GFP_KERNEL);
+ if (new_stage->bh==NULL) {
+ idetape_kfree_stage (new_stage);
+ return (NULL);
+ }
+ new_stage->bh->next=NULL;
+
+ new_stage->bh->data=kmalloc (IDETAPE_ALLOCATION_BLOCK,GFP_KERNEL);
+ if (new_stage->bh->data==NULL) {
+ idetape_kfree_stage (new_stage);
+ return (NULL);
+ }
+
+ for (i=1;i<buffers_num;i++) {
+ bh=(idetape_buffer_head_t *) kmalloc (sizeof (idetape_buffer_head_t),GFP_KERNEL);
+ if (bh==NULL) {
+ idetape_kfree_stage (new_stage);
+ return (NULL);
+ }
+ bh->next=NULL;
+ prev_bh->next=bh;
+ bh->data=kmalloc (IDETAPE_ALLOCATION_BLOCK,GFP_KERNEL);
+ if (bh->data == NULL) {
+ idetape_kfree_stage (new_stage);
+ return (NULL);
+ }
+ prev_bh=bh;
+ }
+ return (new_stage);
+}
+
+/*
+ * idetape_kfree_stage calls kfree to completly free a stage, along with
+ * its related buffers.
+ */
+
+void idetape_kfree_stage (idetape_pipeline_stage_t *stage)
+
+{
+ idetape_buffer_head_t *prev_bh,*bh;
+
+ if (stage == NULL)
+ return;
+
+#if IDETAPE_DEBUG
+ printk ("Reached idetape_kfree_stage\n");
+#endif /* IDETAPE_DEBUG */
+
+ bh=stage->bh;
+
+ while (bh != NULL) {
+ prev_bh=bh;
+ if (bh->data != NULL)
+ kfree (bh->data);
+ bh=bh->next;
+ kfree (prev_bh);
+ }
+
+ kfree (stage);
+ return;
+}
+
+/*
+ * idetape_copy_buffer_from_stage and idetape_copy_buffer_to_stage
+ * copy data from/to the small buffers into/from a continous buffer.
+ */
+
+void idetape_copy_buffer_from_stage (idetape_pipeline_stage_t *stage,char *buffer)
+
+{
+ idetape_buffer_head_t *bh;
+ char *ptr;
+
+#if IDETAPE_DEBUG
+ printk ("Reached idetape_copy_buffer_from_stage\n");
+#endif /* IDETAPE_DEBUG */
+
+ ptr=buffer;
+ bh=stage->bh;
+
+ while (bh != NULL) {
+ memcpy (ptr,bh->data,IDETAPE_ALLOCATION_BLOCK);
+ bh=bh->next;
+ ptr=ptr+IDETAPE_ALLOCATION_BLOCK;
+ }
+ return;
+}
+
+/*
+ * Here we copy a continuous data buffer to the various small buffers
+ * in the pipeline stage.
+ */
+
+void idetape_copy_buffer_to_stage (idetape_pipeline_stage_t *stage,char *buffer)
+
+{
+ idetape_buffer_head_t *bh;
+ char *ptr;
+
+#if IDETAPE_DEBUG
+ printk ("Reached idetape_copy_buffer_to_stage\n");
+#endif /* IDETAPE_DEBUG */
+
+ ptr=buffer;
+ bh=stage->bh;
+
+ while (bh != NULL) {
+ memcpy (bh->data,ptr,IDETAPE_ALLOCATION_BLOCK);
+ bh=bh->next;
+ ptr=ptr+IDETAPE_ALLOCATION_BLOCK;
+ }
+ return;
+}
+
+/*
+ * idetape_increase_max_pipeline_stages is a part of the feedback
+ * loop which tries to find the optimum number of stages. In the
+ * feedback loop, we are starting from a minimum maximum number of
+ * stages, and if we sense that the pipeline is empty, we try to
+ * increase it, until we reach the user compile time memory limit.
+ */
+
+void idetape_increase_max_pipeline_stages (ide_drive_t *drive)
+
+{
+ idetape_tape_t *tape=&(drive->tape);
+
+#if IDETAPE_DEBUG
+ printk ("Reached idetape_increase_max_pipeline_stages\n");
+#endif /* IDETAPE_DEBUG */
+
+ if (!tape->pipeline_was_full_once)
+ return;
+
+ tape->max_number_of_stages+=IDETAPE_INCREASE_STAGES_RATE*
+ (IDETAPE_MAX_PIPELINE_STAGES-IDETAPE_MIN_PIPELINE_STAGES);
+
+ if (tape->max_number_of_stages >= IDETAPE_MAX_PIPELINE_STAGES)
+ tape->max_number_of_stages = IDETAPE_MAX_PIPELINE_STAGES;
+
+#if IDETAPE_DEBUG
+ printk ("Maximum number of stages: %d\n",tape->max_number_of_stages);
+#endif /* IDETAPE_DEBUG */
+
+ return;
+}
+
+/*
+ * idetape_add_stage_tail adds a new stage at the end of the pipeline.
+ */
+
+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
+ printk ("Reached idetape_add_stage_tail\n");
+#endif /* IDETAPE_DEBUG */
+
+ 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)
+ tape->last_stage->next=stage;
+ else
+ tape->first_stage=stage;
+ tape->last_stage=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.
+ */
+
+ 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);
+ }
+ else
+ restore_flags (flags);
+}
+
+/*
+ * idetape_insert_pipeline_into_queue is used to start servicing the
+ * pipeline stages.
+ */
+
+void idetape_insert_pipeline_into_queue (ide_drive_t *drive)
+
+{
+ idetape_tape_t *tape=&(drive->tape);
+
+ if (tape->first_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.
+ */
+
+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;
+
+#if IDETAPE_DEBUG
+ 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");
+ return;
+ }
+#endif /* IDETAPE_DEBUG */
+
+ 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);
}
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