1 /*
2 * Copyright (c) 1998,1999,2000
3 * Traakan, Inc., Los Altos, CA
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice unmodified, this list of conditions, and the following
11 * disclaimer.
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.
15 *
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
26 * SUCH DAMAGE.
27 */
28
29 /*
30 * Project: NDMJOB
31 * Ident: $Id: $
32 *
33 * Description:
34 *
35 */
36
37
38 #include "ndmagents.h"
39
40
41 #ifndef NDMOS_OPTION_NO_TAPE_AGENT
42
43
44 /*
45 * Initialization and Cleanup
46 ****************************************************************
47 */
48
49 /* Initialize -- Set data structure to know value, ignore current value */
50 int
ndmta_initialize(struct ndm_session * sess)51 ndmta_initialize (struct ndm_session *sess)
52 {
53 int rc;
54
55 sess->tape_acb = NDMOS_API_MALLOC (sizeof(struct ndm_tape_agent));
56 if (!sess->tape_acb)
57 return -1;
58 NDMOS_MACRO_ZEROFILL (sess->tape_acb);
59
60 ndmta_commission (sess);
61
62 rc = ndmos_tape_initialize (sess);
63 if (rc) return rc;
64
65 return 0;
66 }
67
68 /* Commission -- Get agent ready. Entire session has been initialize()d */
69 int
ndmta_commission(struct ndm_session * sess)70 ndmta_commission (struct ndm_session *sess)
71 {
72 ndmta_init_mover_state (sess);
73 return 0;
74 }
75
76 /* Decommission -- Discard agent */
77 int
ndmta_decommission(struct ndm_session * sess)78 ndmta_decommission (struct ndm_session *sess)
79 {
80 if (!sess->tape_acb)
81 return 0;
82
83 ndmis_tape_close (sess);
84
85 return 0;
86 }
87
88 /* Destroy -- Destroy agent */
89 int
ndmta_destroy(struct ndm_session * sess)90 ndmta_destroy (struct ndm_session *sess)
91 {
92 if (!sess->tape_acb)
93 return 0;
94
95 if (sess->tape_acb->tape_buffer) {
96 NDMOS_API_FREE (sess->tape_acb->tape_buffer);
97 }
98
99 #ifdef NDMOS_OPTION_TAPE_SIMULATOR
100 if (sess->tape_acb->drive_name) {
101 NDMOS_API_FREE (sess->tape_acb->drive_name);
102 }
103 #endif
104
105 NDMOS_API_FREE (sess->tape_acb);
106 sess->tape_acb = NULL;
107
108 return 0;
109 }
110
111 /* helper for mover state */
112 int
ndmta_init_mover_state(struct ndm_session * sess)113 ndmta_init_mover_state (struct ndm_session *sess)
114 {
115 struct ndm_tape_agent * ta = sess->tape_acb;
116
117 NDMOS_MACRO_ZEROFILL (&ta->mover_state);
118
119 ta->mover_state.state = NDMP9_MOVER_STATE_IDLE;
120 ta->mover_state.window_offset = 0;
121 ta->mover_state.record_num = 0; /* this should probably be -1, but spec says 0 */
122 ta->mover_state.record_size = 20*512; /* traditional tar default */
123 ta->mover_state.window_length = NDMP_LENGTH_INFINITY;
124 ta->mover_window_end = NDMP_LENGTH_INFINITY;
125 ta->mover_want_pos = 0;
126
127 ta->tb_blockno = -1;
128
129 return 0;
130 }
131
132
133
134 /*
135 * Semantic actions -- called from ndma_dispatch()
136 ****************************************************************
137 */
138
139 void
ndmta_mover_sync_state(struct ndm_session * sess)140 ndmta_mover_sync_state (struct ndm_session *sess)
141 {
142 ndmos_tape_sync_state (sess);
143 }
144
145 ndmp9_error
ndmta_mover_listen(struct ndm_session * sess,ndmp9_mover_mode mover_mode)146 ndmta_mover_listen (struct ndm_session *sess, ndmp9_mover_mode mover_mode)
147 {
148 struct ndm_tape_agent * ta = sess->tape_acb;
149
150 ta->mover_state.mode = mover_mode;
151
152 ta->mover_state.state = NDMP9_MOVER_STATE_LISTEN;
153 ta->mover_state.halt_reason = NDMP9_MOVER_HALT_NA;
154 ta->mover_state.pause_reason = NDMP9_MOVER_PAUSE_NA;
155
156 return NDMP9_NO_ERR;
157 }
158
159 ndmp9_error
ndmta_mover_connect(struct ndm_session * sess,ndmp9_mover_mode mover_mode)160 ndmta_mover_connect (struct ndm_session *sess, ndmp9_mover_mode mover_mode)
161 {
162 struct ndm_tape_agent * ta = sess->tape_acb;
163
164 ta->mover_state.mode = mover_mode;
165
166 ndmta_mover_start_active (sess);
167
168 return NDMP9_NO_ERR;
169 }
170
171 void
ndmta_mover_halt(struct ndm_session * sess,ndmp9_mover_halt_reason reason)172 ndmta_mover_halt (struct ndm_session *sess, ndmp9_mover_halt_reason reason)
173 {
174 struct ndm_tape_agent * ta = sess->tape_acb;
175
176 ta->mover_state.state = NDMP9_MOVER_STATE_HALTED;
177 ta->mover_state.halt_reason = reason;
178 ta->mover_state.pause_reason = NDMP9_MOVER_PAUSE_NA;
179 ta->pending_change_after_drain = 0;
180 ta->mover_notify_pending = 1;
181
182 ndmis_tape_close (sess);
183 }
184
185 void
ndmta_mover_pause(struct ndm_session * sess,ndmp9_mover_pause_reason reason)186 ndmta_mover_pause (struct ndm_session *sess, ndmp9_mover_pause_reason reason)
187 {
188 struct ndm_tape_agent * ta = sess->tape_acb;
189
190 ta->mover_state.state = NDMP9_MOVER_STATE_PAUSED;
191 ta->mover_state.halt_reason = NDMP9_MOVER_HALT_NA;
192 ta->mover_state.pause_reason = reason;
193 ta->pending_change_after_drain = 0;
194 ta->mover_notify_pending = 1;
195 }
196
197 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)198 ndmta_mover_pending (struct ndm_session *sess,
199 ndmp9_mover_state pending_state,
200 ndmp9_mover_halt_reason halt_reason,
201 ndmp9_mover_pause_reason pause_reason)
202 {
203 struct ndm_tape_agent * ta = sess->tape_acb;
204
205 if (ta->pending_change_after_drain) {
206 /* internal botch */
207 }
208
209 ta->pending_mover_state = pending_state;
210 ta->pending_mover_halt_reason = halt_reason;
211 ta->pending_mover_pause_reason = pause_reason;
212 ta->pending_change_after_drain = 1;
213 }
214
215 void
ndmta_mover_apply_pending(struct ndm_session * sess)216 ndmta_mover_apply_pending (struct ndm_session *sess)
217 {
218 struct ndm_tape_agent * ta = sess->tape_acb;
219
220 if (!ta->pending_change_after_drain) {
221 /* internal botch */
222 }
223
224 ta->mover_state.state = ta->pending_mover_state;
225 ta->mover_state.halt_reason = ta->pending_mover_halt_reason;
226 ta->mover_state.pause_reason = ta->pending_mover_pause_reason;
227 ta->pending_change_after_drain = 0;
228 ta->mover_notify_pending = 1;
229 }
230
231 void
ndmta_mover_halt_pending(struct ndm_session * sess,ndmp9_mover_halt_reason halt_reason)232 ndmta_mover_halt_pending (struct ndm_session *sess,
233 ndmp9_mover_halt_reason halt_reason)
234 {
235 ndmta_mover_pending (sess, NDMP9_MOVER_STATE_HALTED,
236 halt_reason, NDMP9_MOVER_PAUSE_NA);
237 }
238
239 void
ndmta_mover_pause_pending(struct ndm_session * sess,ndmp9_mover_pause_reason pause_reason)240 ndmta_mover_pause_pending (struct ndm_session *sess,
241 ndmp9_mover_pause_reason pause_reason)
242 {
243 ndmta_mover_pending (sess, NDMP9_MOVER_STATE_PAUSED,
244 NDMP9_MOVER_HALT_NA, pause_reason);
245 }
246
247 void
ndmta_mover_active(struct ndm_session * sess)248 ndmta_mover_active (struct ndm_session *sess)
249 {
250 struct ndm_tape_agent * ta = sess->tape_acb;
251
252 ta->mover_state.state = NDMP9_MOVER_STATE_ACTIVE;
253 ta->mover_state.halt_reason = NDMP9_MOVER_HALT_NA;
254 ta->mover_state.pause_reason = NDMP9_MOVER_PAUSE_NA;
255
256 ta->tb_blockno = -1; /* always mistrust after activating */
257 }
258
259 void
ndmta_mover_start_active(struct ndm_session * sess)260 ndmta_mover_start_active (struct ndm_session *sess)
261 {
262 struct ndm_tape_agent * ta = sess->tape_acb;
263
264 ndmalogf (sess, 0, 6, "mover going active");
265 ndma_send_logmsg(sess, NDMP9_LOG_DEBUG, sess->plumb.control,
266 "mover going active");
267
268 switch (ta->mover_state.mode) {
269 case NDMP9_MOVER_MODE_READ:
270 ndmis_tape_start (sess, NDMCHAN_MODE_READ);
271 ndmta_mover_active (sess);
272 break;
273
274 case NDMP9_MOVER_MODE_WRITE:
275 ndmis_tape_start (sess, NDMCHAN_MODE_WRITE);
276 ndmta_mover_active (sess);
277 break;
278
279 default:
280 ndmalogf (sess, 0, 0, "BOTCH mover listen, unknown mode");
281 break;
282 }
283 }
284
285 void
ndmta_mover_stop(struct ndm_session * sess)286 ndmta_mover_stop (struct ndm_session *sess)
287 {
288 ndmta_init_mover_state (sess);
289 }
290
291 void
ndmta_mover_abort(struct ndm_session * sess)292 ndmta_mover_abort (struct ndm_session *sess)
293 {
294 ndmta_mover_halt (sess, NDMP9_MOVER_HALT_ABORTED);
295 }
296
297 void
ndmta_mover_continue(struct ndm_session * sess)298 ndmta_mover_continue (struct ndm_session *sess)
299 {
300 ndmta_mover_active (sess);
301 }
302
303 void
ndmta_mover_close(struct ndm_session * sess)304 ndmta_mover_close (struct ndm_session *sess)
305 {
306 struct ndm_tape_agent * ta = sess->tape_acb;
307
308 if (ta->mover_state.state != NDMP9_MOVER_STATE_HALTED)
309 ndmta_mover_halt (sess, NDMP9_MOVER_HALT_CONNECT_CLOSED);
310 }
311
312 void
ndmta_mover_read(struct ndm_session * sess,uint64_t offset,uint64_t length)313 ndmta_mover_read (struct ndm_session *sess,
314 uint64_t offset, uint64_t length)
315 {
316 struct ndm_tape_agent * ta = sess->tape_acb;
317
318 ta->mover_state.seek_position = offset;
319 ta->mover_state.bytes_left_to_read = length;
320 ta->mover_want_pos = offset;
321 }
322
323
324
325 /*
326 * Quantum -- get a bit of work done
327 ****************************************************************
328 */
329
330 int
ndmta_quantum(struct ndm_session * sess)331 ndmta_quantum (struct ndm_session *sess)
332 {
333 struct ndm_tape_agent * ta = sess->tape_acb;
334 int rc = 0; /* did nothing */
335
336 switch (ta->mover_state.state) {
337 default:
338 ndmalogf (sess, 0, 0, "BOTCH mover state");
339 return -1;
340
341 case NDMP9_MOVER_STATE_IDLE:
342 case NDMP9_MOVER_STATE_PAUSED:
343 case NDMP9_MOVER_STATE_HALTED:
344 break;
345
346 case NDMP9_MOVER_STATE_LISTEN:
347 switch (sess->plumb.image_stream->tape_ep.connect_status) {
348 case NDMIS_CONN_LISTEN: /* no connection yet */
349 break;
350
351 case NDMIS_CONN_ACCEPTED: /* we're in business */
352 ndmta_mover_start_active (sess);
353 rc = 1; /* did something */
354 break;
355
356 case NDMIS_CONN_BOTCHED: /* accept() went south */
357 default: /* ain't suppose to happen */
358 ndmta_mover_halt(sess,NDMP9_MOVER_HALT_CONNECT_ERROR);
359 break;
360 }
361 break;
362
363 case NDMP9_MOVER_STATE_ACTIVE:
364 switch (ta->mover_state.mode) {
365 case NDMP9_MOVER_MODE_READ:
366 rc = ndmta_read_quantum (sess);
367 break;
368
369 case NDMP9_MOVER_MODE_WRITE:
370 rc = ndmta_write_quantum (sess);
371 break;
372
373 default:
374 ndmalogf (sess, 0, 0,
375 "BOTCH mover active, unknown mode");
376 return -1;
377 }
378 break;
379 }
380
381 ndmta_mover_send_notice (sess);
382
383 return rc;
384 }
385
386 int
ndmta_read_quantum(struct ndm_session * sess)387 ndmta_read_quantum (struct ndm_session *sess)
388 {
389 struct ndm_tape_agent * ta = sess->tape_acb;
390 struct ndmchan * ch = &sess->plumb.image_stream->chan;
391 uint32_t count = ta->mover_state.record_size;
392 int did_something = 0;
393 unsigned n_ready;
394 char * data;
395 uint32_t done_count;
396 ndmp9_error error;
397
398 again:
399 n_ready = ndmchan_n_ready (ch);
400 if (ch->eof) {
401 if (n_ready == 0) {
402 /* done */
403 if (ch->saved_errno)
404 ndmta_mover_halt (sess,
405 NDMP9_MOVER_HALT_CONNECT_ERROR);
406 else
407 ndmta_mover_halt (sess,
408 NDMP9_MOVER_HALT_CONNECT_CLOSED);
409
410 did_something++;
411
412 return did_something;
413 }
414
415 if (n_ready < count) {
416 int n_pad = count - n_ready;
417 int n_avail;
418
419
420 while (n_pad > 0) {
421 n_avail = ndmchan_n_avail (ch);
422 if (n_avail == 0) {
423 /* Uh-oh */
424 }
425 data = &ch->data[ch->end_ix];
426 if (n_avail > n_pad)
427 n_avail = n_pad;
428 bzero (data, n_avail);
429 ch->end_ix += n_avail;
430 n_pad -= n_avail;
431 }
432 n_ready = ndmchan_n_ready (ch);
433 }
434 }
435
436 if (n_ready < count) {
437 return did_something; /* blocked */
438 }
439
440 if (ta->mover_want_pos >= ta->mover_window_end) {
441 ndmta_mover_pause (sess, NDMP9_MOVER_PAUSE_SEEK);
442 did_something++;
443 return did_something;
444 }
445
446 data = &ch->data[ch->beg_ix];
447 done_count = 0;
448
449 error = ndmos_tape_write (sess, data, count, &done_count);
450
451 switch (error) {
452 case NDMP9_NO_ERR:
453 if (done_count != count) {
454 /* This ain't suppose to happen */
455 }
456 ta->mover_state.bytes_moved += count;
457 ta->mover_want_pos += count;
458 ta->mover_state.record_num = ta->mover_want_pos / ta->mover_state.record_size;
459 ch->beg_ix += count;
460 did_something++;
461 goto again; /* write as much to tape as possible */
462
463 case NDMP9_EOM_ERR:
464 ndmta_mover_pause (sess, NDMP9_MOVER_PAUSE_EOM);
465 did_something++;
466 break;
467
468 default:
469 ndmta_mover_halt (sess, NDMP9_MOVER_HALT_MEDIA_ERROR);
470 did_something++;
471 break;
472 }
473
474 return did_something;
475 }
476
477
478 int
ndmta_write_quantum(struct ndm_session * sess)479 ndmta_write_quantum (struct ndm_session *sess)
480 {
481 struct ndm_tape_agent * ta = sess->tape_acb;
482 struct ndmchan * ch = &sess->plumb.image_stream->chan;
483 uint32_t count = ta->mover_state.record_size;
484 int did_something = 0;
485 uint64_t max_read;
486 uint64_t want_window_off;
487 uint32_t block_size;
488 uint32_t want_blockno;
489 uint32_t cur_blockno;
490 unsigned n_avail, n_read, record_off;
491 char * data;
492 uint32_t done_count = 0;
493 ndmp9_error error;
494
495 again:
496 n_read = n_avail = ndmchan_n_avail_record (ch, count);
497 if (n_avail < count) {
498 /* allow to drain */
499 return did_something;
500 }
501
502 if (ta->pending_change_after_drain) {
503 if (ndmchan_n_ready (ch) > 0) {
504 /* allow to drain */
505 } else {
506 ndmta_mover_apply_pending (sess);
507 did_something++;
508 }
509 return did_something;
510 }
511
512 if (n_read > ta->mover_state.bytes_left_to_read)
513 n_read = ta->mover_state.bytes_left_to_read;
514
515 if (n_read < count) {
516 /* Active, but paused awaiting MOVER_READ request */
517 return did_something; /* mover blocked */
518 }
519
520 if (ta->mover_want_pos < ta->mover_state.window_offset
521 || ta->mover_want_pos >= ta->mover_window_end) {
522 ndmta_mover_pause_pending (sess, NDMP9_MOVER_PAUSE_SEEK);
523 goto again;
524 }
525
526 max_read = ta->mover_window_end - ta->mover_want_pos;
527 if (n_read > max_read)
528 n_read = max_read;
529
530 want_window_off = ta->mover_want_pos - ta->mover_state.window_offset;
531
532 /* make an estimate of the block size - the tape agent's block size, or
533 * if it's in variable block size mode, the mover's record size: "When
534 * in variable block mode, as indicated by a tape block_size value of
535 * zero, the mover record size defines the actual block size used by
536 * the tape subsystem." (NDMPv4 RFC, Section 3.6.2.1) */
537 block_size = ta->tape_state.block_size.value;
538 if (!block_size)
539 block_size = ta->mover_state.record_size;
540
541 want_blockno = ta->mover_window_first_blockno + want_window_off / block_size;
542
543 if (ta->tb_blockno != want_blockno) {
544 uint32_t xsr_count = 0;
545 uint32_t xsr_resid = 0;
546
547 ndmos_tape_sync_state(sess);
548 cur_blockno = ta->tape_state.blockno.value;
549 if (cur_blockno < want_blockno) {
550 xsr_count = want_blockno - cur_blockno;
551 ndmalogf (sess, 0, 6, "NDMP9_MTIO_FSR %lu", xsr_count);
552 error = ndmos_tape_mtio (sess, NDMP9_MTIO_FSR,
553 xsr_count, &xsr_resid);
554 if (error == NDMP9_EOF_ERR) {
555 ndmta_mover_pause_pending (sess,
556 NDMP9_MOVER_PAUSE_EOF);
557 goto again;
558 }
559 if (error != NDMP9_NO_ERR) {
560 ndmta_mover_halt_pending (sess,
561 NDMP9_MOVER_HALT_MEDIA_ERROR);
562 goto again;
563 }
564 if (xsr_resid > 0) {
565 ndmta_mover_pause_pending (sess,
566 NDMP9_MOVER_PAUSE_EOF);
567 goto again;
568 }
569 } else if (cur_blockno > want_blockno) {
570 ndmalogf (sess, 0, 6, "NDMP9_MTIO_BSR %lu", xsr_count);
571 xsr_count = cur_blockno - want_blockno;
572 error = ndmos_tape_mtio (sess, NDMP9_MTIO_BSR,
573 xsr_count, &xsr_resid);
574 if (error != NDMP9_NO_ERR || xsr_resid > 0) {
575 ndmta_mover_halt_pending (sess,
576 NDMP9_MOVER_HALT_MEDIA_ERROR);
577 goto again;
578 }
579 } else {
580 /* in position */
581 }
582
583 /*
584 * We are about to read data into a tape buffer so make sure
585 * we have it available. We delay allocating buffers to the
586 * moment we first need them.
587 */
588 if (!ta->tape_buffer) {
589 ta->tape_buffer = NDMOS_API_MALLOC (NDMOS_CONST_TAPE_REC_MAX);
590 if (!ta->tape_buffer) {
591 ndmta_mover_pause_pending (sess,
592 NDMP9_MOVER_HALT_NA);
593 goto again;
594 }
595 }
596
597 data = ta->tape_buffer;
598 done_count = 0;
599 error = ndmos_tape_read (sess, data, count, &done_count);
600 did_something++;
601
602 if (error == NDMP9_EOF_ERR) {
603 ndmta_mover_pause_pending (sess,
604 NDMP9_MOVER_PAUSE_EOF);
605 goto again;
606 }
607 /* N.B. - handling of done_count = 0 here is hacked to support
608 * non-blocking writes to a socket in amndmjob */
609 if (error != NDMP9_NO_ERR) {
610 ndmta_mover_halt_pending (sess,
611 NDMP9_MOVER_HALT_MEDIA_ERROR);
612 goto again;
613 }
614 if (done_count == 0) {
615 return did_something - 1;
616 }
617 if (done_count != count) {
618 goto again;
619 }
620 ta->tb_blockno = want_blockno;
621 /* re-calcluate this, since record_size may be > block_size, in which
622 * case the record_num may not change for each block read from tape */
623 ta->mover_state.record_num = ta->mover_want_pos / ta->mover_state.record_size;
624 }
625
626 record_off = ta->mover_want_pos % ta->mover_state.record_size;
627
628 n_avail = ta->mover_state.record_size - record_off;
629 if (n_read > n_avail)
630 n_read = n_avail;
631 if (n_read != done_count) {
632 printf("lost %lu bytes %lu %u\n", done_count - n_read, done_count, n_read);
633 n_read = done_count;
634 }
635
636 /*
637 * We are about to read data into a tape buffer so make sure
638 * we have it available. We delay allocating buffers to the
639 * moment we first need them.
640 */
641 if (!ta->tape_buffer) {
642 ta->tape_buffer = NDMOS_API_MALLOC (NDMOS_CONST_TAPE_REC_MAX);
643 if (!ta->tape_buffer) {
644 ndmta_mover_pause_pending (sess,
645 NDMP9_MOVER_HALT_NA);
646 goto again;
647 }
648 }
649
650 data = &ta->tape_buffer[record_off];
651
652 bcopy (data, ch->data + ch->end_ix, n_read);
653 ch->end_ix += n_read;
654 ta->mover_state.bytes_moved += n_read;
655 ta->mover_want_pos += n_read;
656 ta->mover_state.bytes_left_to_read -= n_read;
657
658 did_something++;
659
660 goto again; /* do as much as possible */
661 }
662
663 void
ndmta_mover_send_notice(struct ndm_session * sess)664 ndmta_mover_send_notice (struct ndm_session *sess)
665 {
666 struct ndm_tape_agent * ta = sess->tape_acb;
667
668 if (!ta->mover_notify_pending)
669 return;
670
671 ta->mover_notify_pending = 0;
672
673 switch (ta->mover_state.state) {
674 case NDMP9_MOVER_STATE_HALTED:
675 ndma_notify_mover_halted (sess);
676 break;
677
678 case NDMP9_MOVER_STATE_PAUSED:
679 ndma_notify_mover_paused (sess);
680 break;
681
682 default:
683 /* Hmm. Why are we here. Race? */
684 break;
685 }
686 }
687
688 #endif /* !NDMOS_OPTION_NO_TAPE_AGENT */
689