patch-2.1.66 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

diff -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ftape-write.c linux/drivers/char/ftape/ftape-write.c
@@ -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;
-}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov