patch-1.3.72 linux/drivers/char/ftape/ftape-write.c
Next file: linux/drivers/char/ftape/ftape-write.h
Previous file: linux/drivers/char/ftape/ftape-rw.h
Back to the patch index
Back to the overall index
- Lines: 725
- Date:
Wed Mar 6 15:07:20 1996
- Orig file:
v1.3.71/linux/drivers/char/ftape/ftape-write.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v1.3.71/linux/drivers/char/ftape/ftape-write.c linux/drivers/char/ftape/ftape-write.c
@@ -0,0 +1,724 @@
+
+
+
+/*
+ * Copyright (C) 1993-1995 Bas Laarhoven.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-write.c,v $
+ $Author: bas $
+ *
+ $Revision: 1.26 $
+ $Date: 1995/05/27 08:55:27 $
+ $State: Beta $
+ *
+ * This file contains the writing code
+ * for the QIC-117 floppy-tape driver for Linux.
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/ftape.h>
+#include <asm/segment.h>
+
+#include "tracing.h"
+#include "ftape-write.h"
+#include "ftape-read.h"
+#include "qic117.h"
+#include "ftape-io.h"
+#include "ftape-ctl.h"
+#include "ftape-rw.h"
+#include "ftape-eof.h"
+#include "ecc.h"
+#include "ftape-bsm.h"
+
+
+/* Global vars.
+ */
+
+/* Local vars.
+ */
+static int buf_pos_wr = 0;
+static int last_write_failed = 0;
+static int need_flush = 0;
+
+#define WRITE_MULTI 0
+#define WRITE_SINGLE 1
+
+void ftape_zap_write_buffers(void)
+{
+ int i;
+
+ for (i = 0; i < NR_BUFFERS; ++i) {
+ buffer[i].status = done;
+ }
+ need_flush = 0;
+}
+
+int copy_and_gen_ecc(char *destination, byte * source,
+ unsigned int bad_sector_map)
+{
+ TRACE_FUN(8, "copy_and_gen_ecc");
+ int result;
+ struct memory_segment mseg;
+ int bads = count_ones(bad_sector_map);
+
+ if (bads > 0) {
+ TRACEi(4, "bad sectors in map:", bads);
+ }
+ if (bads + 3 >= SECTORS_PER_SEGMENT) {
+ TRACE(4, "empty segment");
+ mseg.blocks = 0; /* skip entire segment */
+ result = 0; /* nothing written */
+ } else {
+ mseg.blocks = SECTORS_PER_SEGMENT - bads;
+ mseg.data = destination;
+ memcpy(mseg.data, source, (mseg.blocks - 3) * SECTOR_SIZE);
+ result = ecc_set_segment_parity(&mseg);
+ if (result < 0) {
+ TRACE(1, "ecc_set_segment_parity failed");
+ } else {
+ result = (mseg.blocks - 3) * SECTOR_SIZE;
+ }
+ }
+ TRACE_EXIT;
+ return result;
+}
+
+void prevent_flush(void)
+{
+ need_flush = 0;
+ ftape_state = idle;
+}
+
+int start_writing(int mode)
+{
+ TRACE_FUN(5, "start_writing");
+ int result = 0;
+ buffer_struct *buff = &buffer[head];
+ int segment_id = buff->segment_id;
+
+ if (ftape_state == writing && buff->status == waiting) {
+ setup_new_segment(buff, segment_id, 1);
+ if (mode == WRITE_SINGLE) {
+ buffer[head].next_segment = 0; /* stop tape instead of pause */
+ }
+ calc_next_cluster(buff); /* prepare */
+ buff->status = writing;
+ if (runner_status == idle) {
+ TRACEi(5, "starting runner for segment", segment_id);
+ result = ftape_start_tape(segment_id, buff->sector_offset);
+ if (result >= 0) {
+ runner_status = running;
+ }
+ }
+ if (result >= 0) {
+ result = setup_fdc_and_dma(buff, FDC_WRITE); /* go */
+ }
+ ftape_state = writing;
+ }
+ TRACE_EXIT;
+ return result;
+}
+
+int loop_until_writes_done(void)
+{
+ TRACE_FUN(5, "loop_until_writes_done");
+ int i;
+ int result = 0;
+
+ /*
+ * Wait until all data is actually written to tape.
+ */
+ while (ftape_state == writing && buffer[head].status != done) {
+ TRACEx2(7, "tail: %d, head: %d", tail, head);
+ for (i = 0; i < NR_BUFFERS; ++i) {
+ TRACEx3(8, "buffer[ %d] segment_id: %d, status: %d",
+ i, buffer[i].segment_id, buffer[i].status);
+ }
+ result = fdc_interrupt_wait(5 * SECOND);
+ if (result < 0) {
+ TRACE(1, "fdc_interrupt_wait failed");
+ last_write_failed = 1;
+ break;
+ }
+ if (buffer[head].status == error) {
+ /* Allow escape from loop when signaled !
+ */
+ if (current->signal & _DONT_BLOCK) {
+ TRACE(2, "interrupted by signal");
+ TRACE_EXIT;
+ result = -EINTR; /* is this the right return value ? */
+ break;
+ }
+ if (buffer[head].hard_error_map != 0) {
+ /* Implement hard write error recovery here
+ */
+ }
+ buffer[head].status = waiting; /* retry this one */
+ if (runner_status == aborting) {
+ ftape_dumb_stop();
+ runner_status = idle;
+ }
+ if (runner_status != idle) {
+ TRACE(1, "unexpected state: runner_status != idle");
+ result = -EIO;
+ break;
+ }
+ start_writing(WRITE_MULTI);
+ }
+ TRACE(5, "looping until writes done");
+ result = 0; /* normal exit status */
+ }
+ TRACE_EXIT;
+ return result;
+}
+
+/* Write given segment from buffer at address onto tape.
+ */
+int write_segment(unsigned segment_id, byte * address, int flushing)
+{
+ TRACE_FUN(5, "write_segment");
+ int result = 0;
+ int bytes_written = 0;
+
+ TRACEi(5, "segment_id =", segment_id);
+ if (ftape_state != writing) {
+ if (ftape_state == reading) {
+ TRACE(5, "calling ftape_abort_operation");
+ result = ftape_abort_operation();
+ if (result < 0) {
+ TRACE(1, "ftape_abort_operation failed");
+ }
+ }
+ ftape_zap_read_buffers();
+ ftape_zap_write_buffers();
+ ftape_state = writing;
+ }
+ /* if all buffers full we'll have to wait...
+ */
+ wait_segment(writing);
+ if (buffer[tail].status == error) {
+ /* setup for a retry
+ */
+ buffer[tail].status = waiting;
+ bytes_written = -EAGAIN; /* force retry */
+ if (buffer[tail].hard_error_map != 0) {
+ TRACEx1(1, "warning: %d hard error(s) in written segment",
+ count_ones(buffer[tail].hard_error_map));
+ TRACEx1(4, "hard_error_map = 0x%08lx", buffer[tail].hard_error_map);
+ /* Implement hard write error recovery here
+ */
+ }
+ } else if (buffer[tail].status == done) {
+ history.defects += count_ones(buffer[tail].hard_error_map);
+ } else {
+ TRACE(1, "wait for empty segment failed");
+ result = -EIO;
+ }
+ /* If just passed last segment on tape: wait for BOT or EOT mark.
+ */
+ if (result >= 0 && runner_status == logical_eot) {
+ int status;
+
+ result = ftape_ready_wait(timeout.seek, &status);
+ if (result < 0 || (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) {
+ TRACE(1, "eot/bot not reached");
+ } else {
+ runner_status = end_of_tape;
+ }
+ }
+ /* should runner stop ?
+ */
+ if (result >= 0 &&
+ (runner_status == aborting || runner_status == buffer_underrun ||
+ runner_status == end_of_tape)) {
+ if (runner_status != end_of_tape) {
+ result = ftape_dumb_stop();
+ }
+ if (result >= 0) {
+ if (runner_status == aborting) {
+ if (buffer[head].status == writing) {
+ buffer[head].status = done; /* ????? */
+ }
+ }
+ runner_status = idle; /* aborted ? */
+ }
+ }
+ /* Don't start tape if runner idle and segment empty.
+ */
+ if (result >= 0 && !(runner_status == idle &&
+ get_bad_sector_entry(segment_id) == EMPTY_SEGMENT)) {
+ if (buffer[tail].status == done) {
+ /* now at least one buffer is empty, fill it with our data.
+ * skip bad sectors and generate ecc.
+ * copy_and_gen_ecc return nr of bytes written,
+ * range 0..29 Kb inclusive !
+ */
+ result = copy_and_gen_ecc(buffer[tail].address, address,
+ get_bad_sector_entry(segment_id));
+ if (result >= 0) {
+ bytes_written = result;
+ buffer[tail].segment_id = segment_id;
+ buffer[tail].status = waiting;
+ next_buffer(&tail);
+ }
+ }
+ /* Start tape only if all buffers full or flush mode.
+ * This will give higher probability of streaming.
+ */
+ if (result >= 0 && runner_status != running &&
+ ((head == tail && buffer[tail].status == waiting) || flushing)) {
+ result = start_writing(WRITE_MULTI);
+ }
+ }
+ TRACE_EXIT;
+ return (result < 0) ? result : bytes_written;
+}
+
+/* Write as much as fits from buffer to the given segment on tape
+ * and handle retries.
+ * Return the number of bytes written (>= 0), or:
+ * -EIO write failed
+ * -EINTR interrupted by signal
+ * -ENOSPC device full
+ */
+int _write_segment(unsigned int segment_id, byte * buffer, int flush)
+{
+ TRACE_FUN(5, "_write_segment");
+ int retry = 0;
+ int result;
+
+ history.used |= 2;
+ for (;;) {
+ if (segment_id > ftape_last_segment.id && !flush) {
+ result = -ENOSPC; /* tape full */
+ break;
+ }
+ result = write_segment(segment_id, buffer, flush);
+ if (result < 0) {
+ if (result == -EAGAIN) {
+ if (++retry > 100) {
+ TRACE(1, "write failed, >100 retries in segment");
+ result = -EIO; /* give up */
+ break;
+ } else {
+ TRACEx1(2, "write error, retry %d", retry);
+ }
+ } else {
+ TRACEi(1, "write_segment failed, error:", -result);
+ break;
+ }
+ } else { /* success */
+ if (result == 0) { /* empty segment */
+ TRACE(4, "empty segment, nothing written");
+ }
+ break;
+ }
+ /* Allow escape from loop when signaled !
+ */
+ if (current->signal & _DONT_BLOCK) {
+ TRACE(2, "interrupted by signal");
+ TRACE_EXIT;
+ result = -EINTR; /* is this the right return value ? */
+ break;
+ }
+ }
+ TRACE_EXIT;
+ return result;
+}
+
+int update_header_segment(unsigned segment, byte * buffer)
+{
+ TRACE_FUN(5, "update_header_segment");
+ int result = 0;
+ int status;
+
+ if (buffer == NULL) {
+ TRACE(5, "no input buffer specified");
+ buffer = deblock_buffer;
+ result = read_segment(used_header_segment, buffer, &status, 0);
+ if (bad_sector_map_changed) {
+ store_bad_sector_map(buffer);
+ }
+ if (failed_sector_log_changed) {
+ update_failed_sector_log(buffer);
+ }
+ }
+ if (result >= 0 && GET4(buffer, 0) != 0xaa55aa55) {
+ TRACE(1, "wrong header signature found, aborting");
+ result = -EIO;
+ }
+ if (result >= 0) {
+ result = _write_segment(segment, buffer, 0);
+ if (result >= 0 && runner_status == idle) {
+ /* Force flush for single segment instead of relying on
+ * flush in read_segment for multiple segments.
+ */
+ result = start_writing(WRITE_SINGLE);
+ if (result >= 0 && ftape_state == writing) {
+ result = loop_until_writes_done();
+ prevent_flush();
+ }
+ }
+#ifdef VERIFY_HEADERS
+ if (result >= 0) { /* read back and verify */
+ result = read_segment(segment, scratch_buffer, &status, 0);
+ /* Should retry if soft error during read !
+ * TO BE IMPLEMENTED
+ */
+ if (result >= 0) {
+ if (memcmp(buffer, scratch_buffer, sizeof(buffer)) == 0) {
+ result = 0; /* verified */
+ TRACE(5, "verified");
+ } else {
+ result = -EIO; /* verify failed */
+ TRACE(5, "verify failed");
+ }
+ }
+ }
+#endif
+ }
+ TRACE_EXIT;
+ return result;
+}
+
+int ftape_write_header_segments(byte * buffer)
+{
+ TRACE_FUN(5, "ftape_write_header_segments");
+ int result = 0;
+ int retry = 0;
+ int header_1_ok = 0;
+ int header_2_ok = 0;
+
+ do {
+ if (!header_1_ok) {
+ result = update_header_segment(header_segment_1, buffer);
+ if (result < 0) {
+ continue;
+ }
+ header_1_ok = 1;
+ }
+ if (!header_2_ok) {
+ result = update_header_segment(header_segment_2, buffer);
+ if (result < 0) {
+ continue;
+ }
+ header_2_ok = 1;
+ }
+ } while (result < 0 && retry++ < 3);
+ if (result < 0) {
+ if (!header_1_ok) {
+ TRACE(1, "update of first header segment failed");
+ }
+ if (!header_2_ok) {
+ TRACE(1, "update of second header segment failed");
+ }
+ result = -EIO;
+ }
+ TRACE_EXIT;
+ return result;
+}
+
+int ftape_update_header_segments(byte * buffer, int update)
+{
+ TRACE_FUN(5, "ftape_update_header_segments");
+ int result = 0;
+ int dummy;
+ int header_changed = 1;
+
+ if (ftape_state == writing) {
+ result = loop_until_writes_done();
+ }
+ if (read_only) {
+ result = 0; /* exit and fake success */
+ TRACE(4, "Tape set read-only: no update");
+ } else if (result >= 0) {
+ result = ftape_abort_operation();
+ if (result >= 0) {
+ if (buffer == NULL) {
+ if (bad_sector_map_changed || failed_sector_log_changed) {
+ ftape_seek_to_bot(); /* prevents extra rewind */
+ buffer = deblock_buffer;
+ result = read_segment(used_header_segment, buffer, &dummy, 0);
+ if (result < 0) {
+ TRACE_EXIT;
+ return result;
+ }
+ }
+ header_changed = 0;
+ }
+ if (update) {
+ if (bad_sector_map_changed) {
+ store_bad_sector_map(buffer);
+ header_changed = 1;
+ }
+ if (failed_sector_log_changed) {
+ update_failed_sector_log(buffer);
+ header_changed = 1;
+ }
+ }
+ if (header_changed) {
+ ftape_seek_to_bot(); /* prevents extra rewind */
+ result = ftape_write_header_segments(buffer);
+ }
+ }
+ }
+ TRACE_EXIT;
+ return result;
+}
+
+int ftape_flush_buffers(void)
+{
+ TRACE_FUN(5, "ftape_flush_buffers");
+ int result;
+ int pad_count;
+ int data_remaining;
+ static int active = 0;
+
+ if (active) {
+ TRACE(5, "nested call, abort");
+ TRACE_EXIT;
+ return 0;
+ }
+ active = 1;
+ TRACEi(5, "entered, ftape_state =", ftape_state);
+ if (ftape_state != writing && !need_flush) {
+ active = 0;
+ TRACE(5, "no need for flush");
+ TRACE_EXIT;
+ return 0;
+ }
+ data_remaining = buf_pos_wr;
+ buf_pos_wr = 0; /* prevent further writes if this fails */
+ TRACE(5, "flushing write buffers");
+ if (last_write_failed) {
+ ftape_zap_write_buffers();
+ active = 0;
+ TRACE_EXIT;
+ return write_protected ? -EROFS : -EIO;
+ }
+ /*
+ * If there is any data not written to tape yet, append zero's
+ * up to the end of the sector. Then write the segment(s) to tape.
+ */
+ if (data_remaining > 0) {
+ int written;
+
+ do {
+ TRACEi(4, "remaining in buffer:", data_remaining);
+ pad_count = sizeof(deblock_buffer) - data_remaining;
+ TRACEi(7, "flush, padding count:", pad_count);
+ memset(deblock_buffer + data_remaining, 0, pad_count); /* pad buffer */
+ result = _write_segment(ftape_seg_pos, deblock_buffer, 1);
+ if (result < 0) {
+ if (result != -ENOSPC) {
+ last_write_failed = 1;
+ }
+ active = 0;
+ TRACE_EXIT;
+ return result;
+ }
+ written = result;
+ clear_eof_mark_if_set(ftape_seg_pos, written);
+ TRACEi(7, "flush, moved out buffer:", written);
+ if (written > 0) {
+ data_remaining -= written;
+ if (data_remaining > 0) {
+ /* Need another segment for remaining data, move the remainder
+ * to the beginning of the buffer
+ */
+ memmove(deblock_buffer, deblock_buffer + written, data_remaining);
+ }
+ }
+ ++ftape_seg_pos;
+ } while (data_remaining > 0);
+ /* Data written to last segment == data_remaining + written
+ * value is in range [1..29K].
+ */
+ TRACEx2(4, "last write: %d, netto pad-count: %d",
+ data_remaining + written, -data_remaining);
+ if (-1024 < data_remaining && data_remaining <= 0) {
+ /* Last sector of segment was used for data, so put eof mark
+ * in next segment and position at second file mark.
+ */
+ if (ftape_weof(2, ftape_seg_pos, 1) >= 0) {
+ ++ftape_seg_pos; /* position between file marks */
+ }
+ } else {
+ /* Put eof mark in previous segment after data and position
+ * at second file mark.
+ */
+ ftape_weof(2, ftape_seg_pos - 1, 1 +
+ ((SECTOR_SIZE - 1 + result + data_remaining) / SECTOR_SIZE));
+ }
+ } else {
+ TRACE(7, "deblock_buffer empty");
+ if (ftape_weof(2, ftape_seg_pos, 1) >= 0) {
+ ++ftape_seg_pos; /* position between file marks */
+ }
+ start_writing(WRITE_MULTI);
+ }
+ TRACE(7, "waiting");
+ result = loop_until_writes_done();
+ if (result < 0) {
+ TRACE(1, "flush buffers failed");
+ }
+ ftape_state = idle;
+ last_write_failed = 0;
+ need_flush = 0;
+ active = 0;
+ TRACE_EXIT;
+ return result;
+}
+
+int _ftape_write(const char *buff, int req_len)
+{
+ TRACE_FUN(5, "_ftape_write");
+ int result = 0;
+ int cnt;
+ int written = 0;
+
+ if (write_protected) {
+ TRACE(1, "error: cartridge write protected");
+ last_write_failed = 1;
+ result = -EROFS;
+ } else if (ftape_offline || !formatted || no_tape) {
+ result = -EIO;
+ } else if (first_data_segment == -1) {
+ /*
+ * If we haven't read the header segment yet, do it now.
+ * This will verify the configuration, get the eof markers
+ * and the bad sector table.
+ * We'll use the deblock buffer for scratch.
+ */
+ result = read_header_segment(deblock_buffer);
+ if (result >= 0 && ftape_seg_pos > ftape_last_segment.id) {
+ result = -ENOSPC; /* full is full */
+ }
+ }
+ if (result < 0) {
+ TRACE_EXIT;
+ return result;
+ }
+ /*
+ * This part writes data blocks to tape until the
+ * requested amount is written.
+ * The data will go in a buffer until it's enough
+ * for a segment without bad sectors. Then we'll write
+ * that segment to tape.
+ * The bytes written will be removed from the buffer
+ * and the process is repeated until there is less
+ * than one segment to write left in the buffer.
+ */
+ while (req_len > 0) {
+ int space_left = sizeof(deblock_buffer) - buf_pos_wr;
+
+ TRACEi(7, "remaining req_len:", req_len);
+ TRACEi(7, " buf_pos:", buf_pos_wr);
+ cnt = (req_len < space_left) ? req_len : space_left;
+ if (cnt > 0) {
+ result = verify_area(VERIFY_READ, buff, cnt);
+ if (result) {
+ TRACE(1, "verify_area failed");
+ last_write_failed = 1;
+ TRACE_EXIT;
+ return result;
+ }
+ memcpy_fromfs(deblock_buffer + buf_pos_wr, buff, cnt);
+ buff += cnt;
+ req_len -= cnt;
+ buf_pos_wr += cnt;
+ }
+ TRACEi(7, "moved into blocking buffer:", cnt);
+ while (buf_pos_wr >= sizeof(deblock_buffer)) {
+ /* If this is the last buffer to be written, let flush handle it.
+ */
+ if (ftape_seg_pos >= ftape_last_segment.id) {
+ TRACEi(7, "remaining in blocking buffer:", buf_pos_wr);
+ TRACEi(7, "just written bytes:", written + cnt);
+ TRACE_EXIT;
+ return written + cnt;
+ }
+ /* Got one full buffer, write it to disk
+ */
+ result = _write_segment(ftape_seg_pos, deblock_buffer, 0);
+ TRACEi(5, "_write_segment result =", result);
+ if (result < 0) {
+ if (result == -EAGAIN) {
+ TRACE(5, "retry...");
+ continue; /* failed, retry same segment */
+ }
+ last_write_failed = 1;
+ TRACE_EXIT;
+ return result;
+ } else {
+ clear_eof_mark_if_set(ftape_seg_pos, result);
+ }
+ if (result > 0 && result < buf_pos_wr) {
+ /* Partial write: move remainder in lower part of buffer
+ */
+ memmove(deblock_buffer, deblock_buffer + result, buf_pos_wr - result);
+ }
+ TRACEi(7, "moved out of blocking buffer:", result);
+ buf_pos_wr -= result; /* remainder */
+ ++ftape_seg_pos;
+ /* Allow us to escape from this loop with a signal !
+ */
+ if (current->signal & _DONT_BLOCK) {
+ TRACE(2, "interrupted by signal");
+ last_write_failed = 1;
+ TRACE_EXIT;
+ return -EINTR; /* is this the right return value ? */
+ }
+ }
+ written += cnt;
+ }
+ TRACEi(7, "remaining in blocking buffer:", buf_pos_wr);
+ TRACEi(7, "just written bytes:", written);
+ last_write_failed = 0;
+ if (!need_flush && written > 0) {
+ need_flush = 1;
+ }
+ TRACE_EXIT;
+ return written; /* bytes written */
+}
+
+int ftape_fix(void)
+{
+ TRACE_FUN(5, "ftape_fix");
+ int result = 0;
+ int dummy;
+ int status;
+
+ if (write_protected) {
+ result = -EROFS;
+ } else {
+ /* This will copy header segment 2 to header segment 1
+ * Spares us a tape format operation if header 2 is still good.
+ */
+ header_segment_1 = 0;
+ header_segment_2 = 1;
+ first_data_segment = 2;
+ result = read_segment(header_segment_2, scratch_buffer, &dummy, 0);
+ result = ftape_ready_wait(timeout.pause, &status);
+ result = ftape_write_header_segments(scratch_buffer);
+ }
+ TRACE_EXIT;
+ return result;
+}
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