2 * Copyright (c) 1998,1999,2000
3 * Traakan, Inc., Los Altos, CA
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice unmodified, this list of conditions, and the following
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 #include "ndmagents.h"
41 #ifndef NDMOS_OPTION_NO_TAPE_AGENT
45 * Initialization and Cleanup
46 ****************************************************************
49 /* Initialize -- Set data structure to know value, ignore current value */
51 ndmta_initialize (struct ndm_session *sess)
53 struct ndm_tape_agent * ta = &sess->tape_acb;
56 NDMOS_MACRO_ZEROFILL(ta);
58 ndmta_commission (sess);
60 rc = ndmos_tape_initialize (sess);
66 /* Commission -- Get agent ready. Entire session has been initialize()d */
68 ndmta_commission (struct ndm_session *sess)
70 ndmta_init_mover_state (sess);
74 /* Decommission -- Discard agent */
76 ndmta_decommission (struct ndm_session *sess)
78 ndmis_tape_close (sess);
79 ndmta_commission (sess);
84 /* helper for mover state */
86 ndmta_init_mover_state (struct ndm_session *sess)
88 struct ndm_tape_agent * ta = &sess->tape_acb;
90 NDMOS_MACRO_ZEROFILL (&ta->mover_state);
92 ta->mover_state.state = NDMP9_MOVER_STATE_IDLE;
93 ta->mover_state.window_offset = 0;
94 ta->mover_state.record_num = 0; /* this should probably be -1, but spec says 0 */
95 ta->mover_state.record_size = 20*512; /* traditional tar default */
96 ta->mover_state.window_length = NDMP_LENGTH_INFINITY;
97 ta->mover_window_end = NDMP_LENGTH_INFINITY;
98 ta->mover_want_pos = 0;
108 * Semantic actions -- called from ndma_dispatch()
109 ****************************************************************
113 ndmta_mover_sync_state (struct ndm_session *sess)
115 ndmos_tape_sync_state (sess);
119 ndmta_mover_listen (struct ndm_session *sess, ndmp9_mover_mode mover_mode)
121 struct ndm_tape_agent * ta = &sess->tape_acb;
123 ta->mover_state.mode = mover_mode;
125 ta->mover_state.state = NDMP9_MOVER_STATE_LISTEN;
126 ta->mover_state.halt_reason = NDMP9_MOVER_HALT_NA;
127 ta->mover_state.pause_reason = NDMP9_MOVER_PAUSE_NA;
133 ndmta_mover_connect (struct ndm_session *sess, ndmp9_mover_mode mover_mode)
135 struct ndm_tape_agent * ta = &sess->tape_acb;
137 ta->mover_state.mode = mover_mode;
139 ndmta_mover_start_active (sess);
145 ndmta_mover_halt (struct ndm_session *sess, ndmp9_mover_halt_reason reason)
147 struct ndm_tape_agent * ta = &sess->tape_acb;
149 ta->mover_state.state = NDMP9_MOVER_STATE_HALTED;
150 ta->mover_state.halt_reason = reason;
151 ta->mover_state.pause_reason = NDMP9_MOVER_PAUSE_NA;
152 ta->pending_change_after_drain = 0;
153 ta->mover_notify_pending = 1;
155 ndmis_tape_close (sess);
159 ndmta_mover_pause (struct ndm_session *sess, ndmp9_mover_pause_reason reason)
161 struct ndm_tape_agent * ta = &sess->tape_acb;
163 ta->mover_state.state = NDMP9_MOVER_STATE_PAUSED;
164 ta->mover_state.halt_reason = NDMP9_MOVER_HALT_NA;
165 ta->mover_state.pause_reason = reason;
166 ta->pending_change_after_drain = 0;
167 ta->mover_notify_pending = 1;
171 ndmta_mover_pending (struct ndm_session *sess,
172 ndmp9_mover_state pending_state,
173 ndmp9_mover_halt_reason halt_reason,
174 ndmp9_mover_pause_reason pause_reason)
176 struct ndm_tape_agent * ta = &sess->tape_acb;
178 if (ta->pending_change_after_drain) {
182 ta->pending_mover_state = pending_state;
183 ta->pending_mover_halt_reason = halt_reason;
184 ta->pending_mover_pause_reason = pause_reason;
185 ta->pending_change_after_drain = 1;
189 ndmta_mover_apply_pending (struct ndm_session *sess)
191 struct ndm_tape_agent * ta = &sess->tape_acb;
193 if (!ta->pending_change_after_drain) {
197 ta->mover_state.state = ta->pending_mover_state;
198 ta->mover_state.halt_reason = ta->pending_mover_halt_reason;
199 ta->mover_state.pause_reason = ta->pending_mover_pause_reason;
200 ta->pending_change_after_drain = 0;
201 ta->mover_notify_pending = 1;
205 ndmta_mover_halt_pending (struct ndm_session *sess,
206 ndmp9_mover_halt_reason halt_reason)
208 ndmta_mover_pending (sess, NDMP9_MOVER_STATE_HALTED,
209 halt_reason, NDMP9_MOVER_PAUSE_NA);
213 ndmta_mover_pause_pending (struct ndm_session *sess,
214 ndmp9_mover_pause_reason pause_reason)
216 ndmta_mover_pending (sess, NDMP9_MOVER_STATE_PAUSED,
217 NDMP9_MOVER_HALT_NA, pause_reason);
221 ndmta_mover_active (struct ndm_session *sess)
223 struct ndm_tape_agent * ta = &sess->tape_acb;
225 ta->mover_state.state = NDMP9_MOVER_STATE_ACTIVE;
226 ta->mover_state.halt_reason = NDMP9_MOVER_HALT_NA;
227 ta->mover_state.pause_reason = NDMP9_MOVER_PAUSE_NA;
229 ta->tb_blockno = -1; /* always mistrust after activating */
233 ndmta_mover_start_active (struct ndm_session *sess)
235 struct ndm_tape_agent * ta = &sess->tape_acb;
237 ndmalogf (sess, 0, 6, "mover going active");
238 ndma_send_logmsg(sess, NDMP9_LOG_DEBUG, sess->plumb.control,
239 "mover going active");
241 switch (ta->mover_state.mode) {
242 case NDMP9_MOVER_MODE_READ:
243 ndmis_tape_start (sess, NDMCHAN_MODE_READ);
244 ndmta_mover_active (sess);
247 case NDMP9_MOVER_MODE_WRITE:
248 ndmis_tape_start (sess, NDMCHAN_MODE_WRITE);
249 ndmta_mover_active (sess);
253 ndmalogf (sess, 0, 0, "BOTCH mover listen, unknown mode");
259 ndmta_mover_stop (struct ndm_session *sess)
261 ndmta_init_mover_state (sess);
265 ndmta_mover_abort (struct ndm_session *sess)
267 ndmta_mover_halt (sess, NDMP9_MOVER_HALT_ABORTED);
271 ndmta_mover_continue (struct ndm_session *sess)
273 ndmta_mover_active (sess);
277 ndmta_mover_close (struct ndm_session *sess)
279 struct ndm_tape_agent * ta = &sess->tape_acb;
281 if (ta->mover_state.state != NDMP9_MOVER_STATE_HALTED)
282 ndmta_mover_halt (sess, NDMP9_MOVER_HALT_CONNECT_CLOSED);
286 ndmta_mover_read (struct ndm_session *sess,
287 unsigned long long offset, unsigned long long length)
289 struct ndm_tape_agent * ta = &sess->tape_acb;
291 ta->mover_state.seek_position = offset;
292 ta->mover_state.bytes_left_to_read = length;
293 ta->mover_want_pos = offset;
299 * Quantum -- get a bit of work done
300 ****************************************************************
304 ndmta_quantum (struct ndm_session *sess)
306 struct ndm_tape_agent * ta = &sess->tape_acb;
307 int rc = 0; /* did nothing */
309 switch (ta->mover_state.state) {
311 ndmalogf (sess, 0, 0, "BOTCH mover state");
314 case NDMP9_MOVER_STATE_IDLE:
315 case NDMP9_MOVER_STATE_PAUSED:
316 case NDMP9_MOVER_STATE_HALTED:
319 case NDMP9_MOVER_STATE_LISTEN:
320 switch (sess->plumb.image_stream.tape_ep.connect_status) {
321 case NDMIS_CONN_LISTEN: /* no connection yet */
324 case NDMIS_CONN_ACCEPTED: /* we're in business */
325 ndmta_mover_start_active (sess);
326 rc = 1; /* did something */
329 case NDMIS_CONN_BOTCHED: /* accept() went south */
330 default: /* ain't suppose to happen */
331 ndmta_mover_halt(sess,NDMP9_MOVER_HALT_CONNECT_ERROR);
336 case NDMP9_MOVER_STATE_ACTIVE:
337 switch (ta->mover_state.mode) {
338 case NDMP9_MOVER_MODE_READ:
339 rc = ndmta_read_quantum (sess);
342 case NDMP9_MOVER_MODE_WRITE:
343 rc = ndmta_write_quantum (sess);
347 ndmalogf (sess, 0, 0,
348 "BOTCH mover active, unknown mode");
354 ndmta_mover_send_notice (sess);
360 ndmta_read_quantum (struct ndm_session *sess)
362 struct ndm_tape_agent * ta = &sess->tape_acb;
363 struct ndmchan * ch = &sess->plumb.image_stream.chan;
364 unsigned long count = ta->mover_state.record_size;
365 int did_something = 0;
368 unsigned long done_count;
372 n_ready = ndmchan_n_ready (ch);
377 ndmta_mover_halt (sess,
378 NDMP9_MOVER_HALT_CONNECT_ERROR);
380 ndmta_mover_halt (sess,
381 NDMP9_MOVER_HALT_CONNECT_CLOSED);
385 return did_something;
388 if (n_ready < count) {
389 int n_pad = count - n_ready;
394 n_avail = ndmchan_n_avail (ch);
398 data = &ch->data[ch->end_ix];
401 bzero (data, n_avail);
402 ch->end_ix += n_avail;
405 n_ready = ndmchan_n_ready (ch);
409 if (n_ready < count) {
410 return did_something; /* blocked */
413 if (ta->mover_want_pos >= ta->mover_window_end) {
414 ndmta_mover_pause (sess, NDMP9_MOVER_PAUSE_SEEK);
416 return did_something;
419 data = &ch->data[ch->beg_ix];
422 error = ndmos_tape_write (sess, data, count, &done_count);
426 if (done_count != count) {
427 /* This ain't suppose to happen */
429 ta->mover_state.bytes_moved += count;
430 /* note this is calculated before mover_want_pos is incremented, since
431 * record_num is the *last* block processed */
432 ta->mover_state.record_num = ta->mover_want_pos / ta->mover_state.record_size;
433 ta->mover_want_pos += count;
436 goto again; /* write as much to tape as possible */
439 ndmta_mover_pause (sess, NDMP9_MOVER_PAUSE_EOM);
444 ndmta_mover_halt (sess, NDMP9_MOVER_HALT_MEDIA_ERROR);
449 return did_something;
454 ndmta_write_quantum (struct ndm_session *sess)
456 struct ndm_tape_agent * ta = &sess->tape_acb;
457 struct ndmchan * ch = &sess->plumb.image_stream.chan;
458 unsigned long count = ta->mover_state.record_size;
459 int did_something = 0;
460 unsigned long long max_read;
461 unsigned long long want_window_off;
462 unsigned long block_size;
463 unsigned long want_blockno;
464 unsigned long cur_blockno;
465 unsigned n_avail, n_read, record_off;
467 unsigned long done_count;
471 n_read = n_avail = ndmchan_n_avail_record (ch, count);
472 if (n_avail < count) {
474 return did_something;
477 if (ta->pending_change_after_drain) {
478 if (ndmchan_n_ready (ch) > 0) {
481 ndmta_mover_apply_pending (sess);
484 return did_something;
487 if (n_read > ta->mover_state.bytes_left_to_read)
488 n_read = ta->mover_state.bytes_left_to_read;
490 if (n_read < count) {
491 /* Active, but paused awaiting MOVER_READ request */
492 return did_something; /* mover blocked */
495 if (ta->mover_want_pos < ta->mover_state.window_offset
496 || ta->mover_want_pos >= ta->mover_window_end) {
497 ndmta_mover_pause_pending (sess, NDMP9_MOVER_PAUSE_SEEK);
501 max_read = ta->mover_window_end - ta->mover_want_pos;
502 if (n_read > max_read)
505 want_window_off = ta->mover_want_pos - ta->mover_state.window_offset;
507 /* make an estimate of the block size - the tape agent's block size, or
508 * if it's in variable block size mode, the mover's record size: "When
509 * in variable block mode, as indicated by a tape block_size value of
510 * zero, the mover record size defines the actual block size used by
511 * the tape subsystem." (NDMPv4 RFC, Section 3.6.2.1) */
512 block_size = ta->tape_state.block_size.value;
514 block_size = ta->mover_state.record_size;
516 want_blockno = ta->mover_window_first_blockno + want_window_off / block_size;
518 if (ta->tb_blockno != want_blockno) {
519 unsigned long xsr_count, xsr_resid;
521 ndmos_tape_sync_state(sess);
522 cur_blockno = ta->tape_state.blockno.value;
523 if (cur_blockno < want_blockno) {
524 xsr_count = want_blockno - cur_blockno;
525 error = ndmos_tape_mtio (sess, NDMP9_MTIO_FSR,
526 xsr_count, &xsr_resid);
527 if (error == NDMP9_EOF_ERR) {
528 ndmta_mover_pause_pending (sess,
529 NDMP9_MOVER_PAUSE_EOF);
532 if (error != NDMP9_NO_ERR) {
533 ndmta_mover_halt_pending (sess,
534 NDMP9_MOVER_HALT_MEDIA_ERROR);
538 ndmta_mover_pause_pending (sess,
539 NDMP9_MOVER_PAUSE_EOF);
542 } else if (cur_blockno > want_blockno) {
543 xsr_count = cur_blockno - want_blockno;
544 error = ndmos_tape_mtio (sess, NDMP9_MTIO_BSR,
545 xsr_count, &xsr_resid);
546 if (error != NDMP9_NO_ERR || xsr_resid > 0) {
547 ndmta_mover_halt_pending (sess,
548 NDMP9_MOVER_HALT_MEDIA_ERROR);
555 data = ta->tape_buffer;
557 error = ndmos_tape_read (sess, data, count, &done_count);
560 if (error == NDMP9_EOF_ERR) {
561 ndmta_mover_pause_pending (sess,
562 NDMP9_MOVER_PAUSE_EOF);
565 /* N.B. - handling of done_count = 0 here is hacked to support
566 * non-blocking writes to a socket in amndmjob */
567 if (error != NDMP9_NO_ERR) {
568 ndmta_mover_halt_pending (sess,
569 NDMP9_MOVER_HALT_MEDIA_ERROR);
572 if (done_count == 0) {
573 return did_something - 1;
575 if (done_count != count) {
579 ta->tb_blockno = want_blockno;
580 /* re-calcluate this, since record_size may be > block_size, in which
581 * case the record_num may not change for each block read from tape */
582 ta->mover_state.record_num = ta->mover_want_pos / ta->mover_state.record_size;
585 record_off = ta->mover_want_pos % ta->mover_state.record_size;
587 n_avail = ta->mover_state.record_size - record_off;
588 if (n_read > n_avail)
590 if (n_read != done_count) {
591 dbprintf("lost %lu bytes %lu %u\n", done_count - n_read, done_count, n_read);
595 data = &ta->tape_buffer[record_off];
597 bcopy (data, ch->data + ch->end_ix, n_read);
598 ch->end_ix += n_read;
599 ta->mover_state.bytes_moved += n_read;
600 ta->mover_want_pos += n_read;
601 ta->mover_state.bytes_left_to_read -= n_read;
605 goto again; /* do as much as possible */
609 ndmta_mover_send_notice (struct ndm_session *sess)
611 struct ndm_tape_agent * ta = &sess->tape_acb;
613 if (!ta->mover_notify_pending)
616 ta->mover_notify_pending = 0;
618 switch (ta->mover_state.state) {
619 case NDMP9_MOVER_STATE_HALTED:
620 ndma_notify_mover_halted (sess);
623 case NDMP9_MOVER_STATE_PAUSED:
624 ndma_notify_mover_paused (sess);
628 /* Hmm. Why are we here. Race? */
633 #endif /* !NDMOS_OPTION_NO_TAPE_AGENT */