X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=ndmp-src%2Fndma_tape.c;fp=ndmp-src%2Fndma_tape.c;h=b27c1cb05e10c8b5ef37eefa580d045ee8d84b36;hb=fd48f3e498442f0cbff5f3606c7c403d0566150e;hp=0000000000000000000000000000000000000000;hpb=96f35b20267e8b1a1c846d476f27fcd330e0b018;p=debian%2Famanda diff --git a/ndmp-src/ndma_tape.c b/ndmp-src/ndma_tape.c new file mode 100644 index 0000000..b27c1cb --- /dev/null +++ b/ndmp-src/ndma_tape.c @@ -0,0 +1,629 @@ +/* + * Copyright (c) 1998,1999,2000 + * Traakan, Inc., Los Altos, CA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Project: NDMJOB + * Ident: $Id: $ + * + * Description: + * + */ + + +#include "ndmagents.h" + + +#ifndef NDMOS_OPTION_NO_TAPE_AGENT + + +/* + * Initialization and Cleanup + **************************************************************** + */ + +/* Initialize -- Set data structure to know value, ignore current value */ +int +ndmta_initialize (struct ndm_session *sess) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + int rc; + + NDMOS_MACRO_ZEROFILL(ta); + + ndmta_commission (sess); + + rc = ndmos_tape_initialize (sess); + if (rc) return rc; + + return 0; +} + +/* Commission -- Get agent ready. Entire session has been initialize()d */ +int +ndmta_commission (struct ndm_session *sess) +{ + ndmta_init_mover_state (sess); + return 0; +} + +/* Decommission -- Discard agent */ +int +ndmta_decommission (struct ndm_session *sess) +{ + ndmis_tape_close (sess); + ndmta_commission (sess); + + return 0; +} + +/* helper for mover state */ +int +ndmta_init_mover_state (struct ndm_session *sess) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + + NDMOS_MACRO_ZEROFILL (&ta->mover_state); + + ta->mover_state.state = NDMP9_MOVER_STATE_IDLE; + ta->mover_state.window_offset = 0; + ta->mover_state.record_num = 0; /* this should probably be -1, but spec says 0 */ + ta->mover_state.record_size = 20*512; /* traditional tar default */ + ta->mover_state.window_length = NDMP_LENGTH_INFINITY; + ta->mover_window_end = NDMP_LENGTH_INFINITY; + ta->mover_want_pos = 0; + + ta->tb_blockno = -1; + + return 0; +} + + + +/* + * Semantic actions -- called from ndma_dispatch() + **************************************************************** + */ + +void +ndmta_mover_sync_state (struct ndm_session *sess) +{ + ndmos_tape_sync_state (sess); +} + +ndmp9_error +ndmta_mover_listen (struct ndm_session *sess, ndmp9_mover_mode mover_mode) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + + ta->mover_state.mode = mover_mode; + + ta->mover_state.state = NDMP9_MOVER_STATE_LISTEN; + ta->mover_state.halt_reason = NDMP9_MOVER_HALT_NA; + ta->mover_state.pause_reason = NDMP9_MOVER_PAUSE_NA; + + return NDMP9_NO_ERR; +} + +ndmp9_error +ndmta_mover_connect (struct ndm_session *sess, ndmp9_mover_mode mover_mode) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + + ta->mover_state.mode = mover_mode; + + ndmta_mover_start_active (sess); + + return NDMP9_NO_ERR; +} + +void +ndmta_mover_halt (struct ndm_session *sess, ndmp9_mover_halt_reason reason) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + + ta->mover_state.state = NDMP9_MOVER_STATE_HALTED; + ta->mover_state.halt_reason = reason; + ta->mover_state.pause_reason = NDMP9_MOVER_PAUSE_NA; + ta->pending_change_after_drain = 0; + ta->mover_notify_pending = 1; + + ndmis_tape_close (sess); +} + +void +ndmta_mover_pause (struct ndm_session *sess, ndmp9_mover_pause_reason reason) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + + ta->mover_state.state = NDMP9_MOVER_STATE_PAUSED; + ta->mover_state.halt_reason = NDMP9_MOVER_HALT_NA; + ta->mover_state.pause_reason = reason; + ta->pending_change_after_drain = 0; + ta->mover_notify_pending = 1; +} + +void +ndmta_mover_pending (struct ndm_session *sess, + ndmp9_mover_state pending_state, + ndmp9_mover_halt_reason halt_reason, + ndmp9_mover_pause_reason pause_reason) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + + if (ta->pending_change_after_drain) { + /* internal botch */ + } + + ta->pending_mover_state = pending_state; + ta->pending_mover_halt_reason = halt_reason; + ta->pending_mover_pause_reason = pause_reason; + ta->pending_change_after_drain = 1; +} + +void +ndmta_mover_apply_pending (struct ndm_session *sess) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + + if (!ta->pending_change_after_drain) { + /* internal botch */ + } + + ta->mover_state.state = ta->pending_mover_state; + ta->mover_state.halt_reason = ta->pending_mover_halt_reason; + ta->mover_state.pause_reason = ta->pending_mover_pause_reason; + ta->pending_change_after_drain = 0; + ta->mover_notify_pending = 1; +} + +void +ndmta_mover_halt_pending (struct ndm_session *sess, + ndmp9_mover_halt_reason halt_reason) +{ + ndmta_mover_pending (sess, NDMP9_MOVER_STATE_HALTED, + halt_reason, NDMP9_MOVER_PAUSE_NA); +} + +void +ndmta_mover_pause_pending (struct ndm_session *sess, + ndmp9_mover_pause_reason pause_reason) +{ + ndmta_mover_pending (sess, NDMP9_MOVER_STATE_PAUSED, + NDMP9_MOVER_HALT_NA, pause_reason); +} + +void +ndmta_mover_active (struct ndm_session *sess) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + + ta->mover_state.state = NDMP9_MOVER_STATE_ACTIVE; + ta->mover_state.halt_reason = NDMP9_MOVER_HALT_NA; + ta->mover_state.pause_reason = NDMP9_MOVER_PAUSE_NA; + + ta->tb_blockno = -1; /* always mistrust after activating */ +} + +void +ndmta_mover_start_active (struct ndm_session *sess) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + + ndmalogf (sess, 0, 6, "mover going active"); + ndma_send_logmsg(sess, NDMP9_LOG_DEBUG, sess->plumb.control, + "mover going active"); + + switch (ta->mover_state.mode) { + case NDMP9_MOVER_MODE_READ: + ndmis_tape_start (sess, NDMCHAN_MODE_READ); + ndmta_mover_active (sess); + break; + + case NDMP9_MOVER_MODE_WRITE: + ndmis_tape_start (sess, NDMCHAN_MODE_WRITE); + ndmta_mover_active (sess); + break; + + default: + ndmalogf (sess, 0, 0, "BOTCH mover listen, unknown mode"); + break; + } +} + +void +ndmta_mover_stop (struct ndm_session *sess) +{ + ndmta_init_mover_state (sess); +} + +void +ndmta_mover_abort (struct ndm_session *sess) +{ + ndmta_mover_halt (sess, NDMP9_MOVER_HALT_ABORTED); +} + +void +ndmta_mover_continue (struct ndm_session *sess) +{ + ndmta_mover_active (sess); +} + +void +ndmta_mover_close (struct ndm_session *sess) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + + if (ta->mover_state.state != NDMP9_MOVER_STATE_HALTED) + ndmta_mover_halt (sess, NDMP9_MOVER_HALT_CONNECT_CLOSED); +} + +void +ndmta_mover_read (struct ndm_session *sess, + unsigned long long offset, unsigned long long length) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + + ta->mover_state.seek_position = offset; + ta->mover_state.bytes_left_to_read = length; + ta->mover_want_pos = offset; +} + + + +/* + * Quantum -- get a bit of work done + **************************************************************** + */ + +int +ndmta_quantum (struct ndm_session *sess) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + int rc = 0; /* did nothing */ + + switch (ta->mover_state.state) { + default: + ndmalogf (sess, 0, 0, "BOTCH mover state"); + return -1; + + case NDMP9_MOVER_STATE_IDLE: + case NDMP9_MOVER_STATE_PAUSED: + case NDMP9_MOVER_STATE_HALTED: + break; + + case NDMP9_MOVER_STATE_LISTEN: + switch (sess->plumb.image_stream.tape_ep.connect_status) { + case NDMIS_CONN_LISTEN: /* no connection yet */ + break; + + case NDMIS_CONN_ACCEPTED: /* we're in business */ + ndmta_mover_start_active (sess); + rc = 1; /* did something */ + break; + + case NDMIS_CONN_BOTCHED: /* accept() went south */ + default: /* ain't suppose to happen */ + ndmta_mover_halt(sess,NDMP9_MOVER_HALT_CONNECT_ERROR); + break; + } + break; + + case NDMP9_MOVER_STATE_ACTIVE: + switch (ta->mover_state.mode) { + case NDMP9_MOVER_MODE_READ: + rc = ndmta_read_quantum (sess); + break; + + case NDMP9_MOVER_MODE_WRITE: + rc = ndmta_write_quantum (sess); + break; + + default: + ndmalogf (sess, 0, 0, + "BOTCH mover active, unknown mode"); + return -1; + } + break; + } + + ndmta_mover_send_notice (sess); + + return rc; +} + +int +ndmta_read_quantum (struct ndm_session *sess) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + struct ndmchan * ch = &sess->plumb.image_stream.chan; + unsigned long count = ta->mover_state.record_size; + int did_something = 0; + unsigned n_ready; + char * data; + unsigned long done_count; + ndmp9_error error; + + again: + n_ready = ndmchan_n_ready (ch); + if (ch->eof) { + if (n_ready == 0) { + /* done */ + if (ch->saved_errno) + ndmta_mover_halt (sess, + NDMP9_MOVER_HALT_CONNECT_ERROR); + else + ndmta_mover_halt (sess, + NDMP9_MOVER_HALT_CONNECT_CLOSED); + + did_something++; + + return did_something; + } + + if (n_ready < count) { + int n_pad = count - n_ready; + int n_avail; + + + while (n_pad > 0) { + n_avail = ndmchan_n_avail (ch); + if (n_avail == 0) { + /* Uh-oh */ + } + data = &ch->data[ch->end_ix]; + if (n_avail > n_pad) + n_avail = n_pad; + bzero (data, n_avail); + ch->end_ix += n_avail; + n_pad -= n_avail; + } + n_ready = ndmchan_n_ready (ch); + } + } + + if (n_ready < count) { + return did_something; /* blocked */ + } + + if (ta->mover_want_pos >= ta->mover_window_end) { + ndmta_mover_pause (sess, NDMP9_MOVER_PAUSE_SEEK); + did_something++; + return did_something; + } + + data = &ch->data[ch->beg_ix]; + done_count = 0; + + error = ndmos_tape_write (sess, data, count, &done_count); + + switch (error) { + case NDMP9_NO_ERR: + if (done_count != count) { + /* This ain't suppose to happen */ + } + ta->mover_state.bytes_moved += count; + /* note this is calculated before mover_want_pos is incremented, since + * record_num is the *last* block processed */ + ta->mover_state.record_num = ta->mover_want_pos / ta->mover_state.record_size; + ta->mover_want_pos += count; + ch->beg_ix += count; + did_something++; + goto again; /* write as much to tape as possible */ + + case NDMP9_EOM_ERR: + ndmta_mover_pause (sess, NDMP9_MOVER_PAUSE_EOM); + did_something++; + break; + + default: + ndmta_mover_halt (sess, NDMP9_MOVER_HALT_MEDIA_ERROR); + did_something++; + break; + } + + return did_something; +} + + +int +ndmta_write_quantum (struct ndm_session *sess) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + struct ndmchan * ch = &sess->plumb.image_stream.chan; + unsigned long count = ta->mover_state.record_size; + int did_something = 0; + unsigned long long max_read; + unsigned long long want_window_off; + unsigned long block_size; + unsigned long want_blockno; + unsigned long cur_blockno; + unsigned n_avail, n_read, record_off; + char * data; + unsigned long done_count; + ndmp9_error error; + + again: + n_read = n_avail = ndmchan_n_avail (ch); + if (n_avail == 0) { + /* allow to drain */ + return did_something; + } + + if (ta->pending_change_after_drain) { + if (ndmchan_n_ready (ch) > 0) { + /* allow to drain */ + } else { + ndmta_mover_apply_pending (sess); + did_something++; + } + return did_something; + } + + if (n_read > ta->mover_state.bytes_left_to_read) + n_read = ta->mover_state.bytes_left_to_read; + + if (n_read == 0) { + /* Active, but paused awaiting MOVER_READ request */ + return did_something; /* mover blocked */ + } + + if (ta->mover_want_pos < ta->mover_state.window_offset + || ta->mover_want_pos >= ta->mover_window_end) { + ndmta_mover_pause_pending (sess, NDMP9_MOVER_PAUSE_SEEK); + goto again; + } + + max_read = ta->mover_window_end - ta->mover_want_pos; + if (n_read > max_read) + n_read = max_read; + + want_window_off = ta->mover_want_pos - ta->mover_state.window_offset; + + /* make an estimate of the block size - the tape agent's block size, or + * if it's in variable block size mode, the mover's record size: "When + * in variable block mode, as indicated by a tape block_size value of + * zero, the mover record size defines the actual block size used by + * the tape subsystem." (NDMPv4 RFC, Section 3.6.2.1) */ + block_size = ta->tape_state.block_size.value; + if (!block_size) + block_size = ta->mover_state.record_size; + + want_blockno = ta->mover_window_first_blockno + want_window_off / block_size; + + if (ta->tb_blockno != want_blockno) { + unsigned long xsr_count, xsr_resid; + + ndmos_tape_sync_state(sess); + cur_blockno = ta->tape_state.blockno.value; + if (cur_blockno < want_blockno) { + xsr_count = want_blockno - cur_blockno; + error = ndmos_tape_mtio (sess, NDMP9_MTIO_FSR, + xsr_count, &xsr_resid); + if (error == NDMP9_EOF_ERR) { + ndmta_mover_pause_pending (sess, + NDMP9_MOVER_PAUSE_EOF); + goto again; + } + if (error != NDMP9_NO_ERR) { + ndmta_mover_halt_pending (sess, + NDMP9_MOVER_HALT_MEDIA_ERROR); + goto again; + } + if (xsr_resid > 0) { + ndmta_mover_pause_pending (sess, + NDMP9_MOVER_PAUSE_EOF); + goto again; + } + } else if (cur_blockno > want_blockno) { + xsr_count = cur_blockno - want_blockno; + error = ndmos_tape_mtio (sess, NDMP9_MTIO_BSR, + xsr_count, &xsr_resid); + if (error != NDMP9_NO_ERR || xsr_resid > 0) { + ndmta_mover_halt_pending (sess, + NDMP9_MOVER_HALT_MEDIA_ERROR); + goto again; + } + } else { + /* in position */ + } + + data = ta->tape_buffer; + done_count = 0; + error = ndmos_tape_read (sess, data, count, &done_count); + did_something++; + + if (error == NDMP9_EOF_ERR) { + ndmta_mover_pause_pending (sess, + NDMP9_MOVER_PAUSE_EOF); + goto again; + } + /* N.B. - handling of done_count = 0 here is hacked to support + * non-blocking writes to a socket in amndmjob */ + if (error != NDMP9_NO_ERR) { + ndmta_mover_halt_pending (sess, + NDMP9_MOVER_HALT_MEDIA_ERROR); + goto again; + } + if (done_count == 0) { + return did_something - 1; + } + if (done_count != count) { + n_read = done_count; + goto again; + } + ta->tb_blockno = want_blockno; + /* re-calcluate this, since record_size may be > block_size, in which + * case the record_num may not change for each block read from tape */ + ta->mover_state.record_num = ta->mover_want_pos / ta->mover_state.record_size; + } + + record_off = ta->mover_want_pos % ta->mover_state.record_size; + + n_avail = ta->mover_state.record_size - record_off; + if (n_read > n_avail) + n_read = n_avail; + + data = &ta->tape_buffer[record_off]; + + bcopy (data, ch->data + ch->end_ix, n_read); + ch->end_ix += n_read; + ta->mover_state.bytes_moved += n_read; + ta->mover_want_pos += n_read; + ta->mover_state.bytes_left_to_read -= n_read; + + did_something++; + + goto again; /* do as much as possible */ +} + +void +ndmta_mover_send_notice (struct ndm_session *sess) +{ + struct ndm_tape_agent * ta = &sess->tape_acb; + + if (!ta->mover_notify_pending) + return; + + ta->mover_notify_pending = 0; + + switch (ta->mover_state.state) { + case NDMP9_MOVER_STATE_HALTED: + ndma_notify_mover_halted (sess); + break; + + case NDMP9_MOVER_STATE_PAUSED: + ndma_notify_mover_paused (sess); + break; + + default: + /* Hmm. Why are we here. Race? */ + break; + } +} + +#endif /* !NDMOS_OPTION_NO_TAPE_AGENT */