diff options
Diffstat (limited to 'drivers/char/ftape/ftape-write.c')
-rw-r--r-- | drivers/char/ftape/ftape-write.c | 723 |
1 files changed, 0 insertions, 723 deletions
diff --git a/drivers/char/ftape/ftape-write.c b/drivers/char/ftape/ftape-write.c deleted file mode 100644 index 0ba24c978..000000000 --- a/drivers/char/ftape/ftape-write.c +++ /dev/null @@ -1,723 +0,0 @@ - - - -/* - * 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/string.h> -#include <linux/errno.h> -#include <linux/mm.h> -#include <linux/ftape.h> -#include <asm/uaccess.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; - } - copy_from_user(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; -} |