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 struct ndm_tape_agent * ta = &sess->tape_acb;
54 int rc;
55
56 NDMOS_MACRO_ZEROFILL(ta);
57
58 ndmta_commission (sess);
59
60 rc = ndmos_tape_initialize (sess);
61 if (rc) return rc;
62
63 return 0;
64 }
65
66 /* Commission -- Get agent ready. Entire session has been initialize()d */
67 int
ndmta_commission(struct ndm_session * sess)68 ndmta_commission (struct ndm_session *sess)
69 {
70 ndmta_init_mover_state (sess);
71 return 0;
72 }
73
74 /* Decommission -- Discard agent */
75 int
ndmta_decommission(struct ndm_session * sess)76 ndmta_decommission (struct ndm_session *sess)
77 {
78 ndmis_tape_close (sess);
79 ndmta_commission (sess);
80
81 return 0;
82 }
83
84 /* helper for mover state */
85 int
ndmta_init_mover_state(struct ndm_session * sess)86 ndmta_init_mover_state (struct ndm_session *sess)
87 {
88 struct ndm_tape_agent * ta = &sess->tape_acb;
89
90 NDMOS_MACRO_ZEROFILL (&ta->mover_state);
91
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;
99
100 ta->tb_blockno = -1;
101
102 return 0;
103 }
104
105
106
107 /*
108 * Semantic actions -- called from ndma_dispatch()
109 ****************************************************************
110 */
111
112 void
ndmta_mover_sync_state(struct ndm_session * sess)113 ndmta_mover_sync_state (struct ndm_session *sess)
114 {
115 ndmos_tape_sync_state (sess);
116 }
117
118 ndmp9_error
ndmta_mover_listen(struct ndm_session * sess,ndmp9_mover_mode mover_mode)119 ndmta_mover_listen (struct ndm_session *sess, ndmp9_mover_mode mover_mode)
120 {
121 struct ndm_tape_agent * ta = &sess->tape_acb;
122
123 ta->mover_state.mode = mover_mode;
124
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;
128
129 return NDMP9_NO_ERR;
130 }
131
132 ndmp9_error
ndmta_mover_connect(struct ndm_session * sess,ndmp9_mover_mode mover_mode)133 ndmta_mover_connect (struct ndm_session *sess, ndmp9_mover_mode mover_mode)
134 {
135 struct ndm_tape_agent * ta = &sess->tape_acb;
136
137 ta->mover_state.mode = mover_mode;
138
139 ndmta_mover_start_active (sess);
140
141 return NDMP9_NO_ERR;
142 }
143
144 void
ndmta_mover_halt(struct ndm_session * sess,ndmp9_mover_halt_reason reason)145 ndmta_mover_halt (struct ndm_session *sess, ndmp9_mover_halt_reason reason)
146 {
147 struct ndm_tape_agent * ta = &sess->tape_acb;
148
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;
154
155 ndmis_tape_close (sess);
156 }
157
158 void
ndmta_mover_pause(struct ndm_session * sess,ndmp9_mover_pause_reason reason)159 ndmta_mover_pause (struct ndm_session *sess, ndmp9_mover_pause_reason reason)
160 {
161 struct ndm_tape_agent * ta = &sess->tape_acb;
162
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;
168 }
169
170 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)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)
175 {
176 struct ndm_tape_agent * ta = &sess->tape_acb;
177
178 if (ta->pending_change_after_drain) {
179 /* internal botch */
180 }
181
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;
186 }
187
188 void
ndmta_mover_apply_pending(struct ndm_session * sess)189 ndmta_mover_apply_pending (struct ndm_session *sess)
190 {
191 struct ndm_tape_agent * ta = &sess->tape_acb;
192
193 if (!ta->pending_change_after_drain) {
194 /* internal botch */
195 }
196
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;
202 }
203
204 void
ndmta_mover_halt_pending(struct ndm_session * sess,ndmp9_mover_halt_reason halt_reason)205 ndmta_mover_halt_pending (struct ndm_session *sess,
206 ndmp9_mover_halt_reason halt_reason)
207 {
208 ndmta_mover_pending (sess, NDMP9_MOVER_STATE_HALTED,
209 halt_reason, NDMP9_MOVER_PAUSE_NA);
210 }
211
212 void
ndmta_mover_pause_pending(struct ndm_session * sess,ndmp9_mover_pause_reason pause_reason)213 ndmta_mover_pause_pending (struct ndm_session *sess,
214 ndmp9_mover_pause_reason pause_reason)
215 {
216 ndmta_mover_pending (sess, NDMP9_MOVER_STATE_PAUSED,
217 NDMP9_MOVER_HALT_NA, pause_reason);
218 }
219
220 void
ndmta_mover_active(struct ndm_session * sess)221 ndmta_mover_active (struct ndm_session *sess)
222 {
223 struct ndm_tape_agent * ta = &sess->tape_acb;
224
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;
228
229 ta->tb_blockno = -1; /* always mistrust after activating */
230 }
231
232 void
ndmta_mover_start_active(struct ndm_session * sess)233 ndmta_mover_start_active (struct ndm_session *sess)
234 {
235 struct ndm_tape_agent * ta = &sess->tape_acb;
236
237 ndmalogf (sess, 0, 6, "mover going active");
238 ndma_send_logmsg(sess, NDMP9_LOG_DEBUG, sess->plumb.control,
239 "mover going active");
240
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);
245 break;
246
247 case NDMP9_MOVER_MODE_WRITE:
248 ndmis_tape_start (sess, NDMCHAN_MODE_WRITE);
249 ndmta_mover_active (sess);
250 break;
251
252 default:
253 ndmalogf (sess, 0, 0, "BOTCH mover listen, unknown mode");
254 break;
255 }
256 }
257
258 void
ndmta_mover_stop(struct ndm_session * sess)259 ndmta_mover_stop (struct ndm_session *sess)
260 {
261 ndmta_init_mover_state (sess);
262 }
263
264 void
ndmta_mover_abort(struct ndm_session * sess)265 ndmta_mover_abort (struct ndm_session *sess)
266 {
267 ndmta_mover_halt (sess, NDMP9_MOVER_HALT_ABORTED);
268 }
269
270 void
ndmta_mover_continue(struct ndm_session * sess)271 ndmta_mover_continue (struct ndm_session *sess)
272 {
273 ndmta_mover_active (sess);
274 }
275
276 void
ndmta_mover_close(struct ndm_session * sess)277 ndmta_mover_close (struct ndm_session *sess)
278 {
279 struct ndm_tape_agent * ta = &sess->tape_acb;
280
281 if (ta->mover_state.state != NDMP9_MOVER_STATE_HALTED)
282 ndmta_mover_halt (sess, NDMP9_MOVER_HALT_CONNECT_CLOSED);
283 }
284
285 void
ndmta_mover_read(struct ndm_session * sess,unsigned long long offset,unsigned long long length)286 ndmta_mover_read (struct ndm_session *sess,
287 unsigned long long offset, unsigned long long length)
288 {
289 struct ndm_tape_agent * ta = &sess->tape_acb;
290
291 ta->mover_state.seek_position = offset;
292 ta->mover_state.bytes_left_to_read = length;
293 ta->mover_want_pos = offset;
294 }
295
296
297
298 /*
299 * Quantum -- get a bit of work done
300 ****************************************************************
301 */
302
303 int
ndmta_quantum(struct ndm_session * sess)304 ndmta_quantum (struct ndm_session *sess)
305 {
306 struct ndm_tape_agent * ta = &sess->tape_acb;
307 int rc = 0; /* did nothing */
308
309 switch (ta->mover_state.state) {
310 default:
311 ndmalogf (sess, 0, 0, "BOTCH mover state");
312 return -1;
313
314 case NDMP9_MOVER_STATE_IDLE:
315 case NDMP9_MOVER_STATE_PAUSED:
316 case NDMP9_MOVER_STATE_HALTED:
317 break;
318
319 case NDMP9_MOVER_STATE_LISTEN:
320 switch (sess->plumb.image_stream.tape_ep.connect_status) {
321 case NDMIS_CONN_LISTEN: /* no connection yet */
322 break;
323
324 case NDMIS_CONN_ACCEPTED: /* we're in business */
325 ndmta_mover_start_active (sess);
326 rc = 1; /* did something */
327 break;
328
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);
332 break;
333 }
334 break;
335
336 case NDMP9_MOVER_STATE_ACTIVE:
337 switch (ta->mover_state.mode) {
338 case NDMP9_MOVER_MODE_READ:
339 rc = ndmta_read_quantum (sess);
340 break;
341
342 case NDMP9_MOVER_MODE_WRITE:
343 rc = ndmta_write_quantum (sess);
344 break;
345
346 default:
347 ndmalogf (sess, 0, 0,
348 "BOTCH mover active, unknown mode");
349 return -1;
350 }
351 break;
352 }
353
354 ndmta_mover_send_notice (sess);
355
356 return rc;
357 }
358
359 int
ndmta_read_quantum(struct ndm_session * sess)360 ndmta_read_quantum (struct ndm_session *sess)
361 {
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;
366 unsigned n_ready;
367 char * data;
368 unsigned long done_count;
369 ndmp9_error error;
370
371 again:
372 n_ready = ndmchan_n_ready (ch);
373 if (ch->eof) {
374 if (n_ready == 0) {
375 /* done */
376 if (ch->saved_errno)
377 ndmta_mover_halt (sess,
378 NDMP9_MOVER_HALT_CONNECT_ERROR);
379 else
380 ndmta_mover_halt (sess,
381 NDMP9_MOVER_HALT_CONNECT_CLOSED);
382
383 did_something++;
384
385 return did_something;
386 }
387
388 if (n_ready < count) {
389 int n_pad = count - n_ready;
390 int n_avail;
391
392
393 while (n_pad > 0) {
394 n_avail = ndmchan_n_avail (ch);
395 if (n_avail == 0) {
396 /* Uh-oh */
397 }
398 data = &ch->data[ch->end_ix];
399 if (n_avail > n_pad)
400 n_avail = n_pad;
401 bzero (data, n_avail);
402 ch->end_ix += n_avail;
403 n_pad -= n_avail;
404 }
405 n_ready = ndmchan_n_ready (ch);
406 }
407 }
408
409 if (n_ready < count) {
410 return did_something; /* blocked */
411 }
412
413 if (ta->mover_want_pos >= ta->mover_window_end) {
414 ndmta_mover_pause (sess, NDMP9_MOVER_PAUSE_SEEK);
415 did_something++;
416 return did_something;
417 }
418
419 data = &ch->data[ch->beg_ix];
420 done_count = 0;
421
422 error = ndmos_tape_write (sess, data, count, &done_count);
423
424 switch (error) {
425 case NDMP9_NO_ERR:
426 if (done_count != count) {
427 /* This ain't suppose to happen */
428 }
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 /* It was a bug, set record_num after mover_want_pos */
434 ta->mover_want_pos += count;
435 ta->mover_state.record_num = ta->mover_want_pos / ta->mover_state.record_size;
436 ch->beg_ix += count;
437 did_something++;
438 goto again; /* write as much to tape as possible */
439
440 case NDMP9_EOM_ERR:
441 ndmta_mover_pause (sess, NDMP9_MOVER_PAUSE_EOM);
442 did_something++;
443 break;
444
445 default:
446 ndmta_mover_halt (sess, NDMP9_MOVER_HALT_MEDIA_ERROR);
447 did_something++;
448 break;
449 }
450
451 return did_something;
452 }
453
454
455 int
ndmta_write_quantum(struct ndm_session * sess)456 ndmta_write_quantum (struct ndm_session *sess)
457 {
458 struct ndm_tape_agent * ta = &sess->tape_acb;
459 struct ndmchan * ch = &sess->plumb.image_stream.chan;
460 unsigned long count = ta->mover_state.record_size;
461 int did_something = 0;
462 unsigned long long max_read;
463 unsigned long long want_window_off;
464 unsigned long block_size;
465 unsigned long want_blockno;
466 unsigned long cur_blockno;
467 unsigned n_avail, n_read, record_off;
468 char * data;
469 unsigned long done_count;
470 ndmp9_error error;
471
472 again:
473 n_read = n_avail = ndmchan_n_avail_record (ch, count);
474 if (n_avail < count) {
475 /* allow to drain */
476 return did_something;
477 }
478
479 if (ta->pending_change_after_drain) {
480 if (ndmchan_n_ready (ch) > 0) {
481 /* allow to drain */
482 } else {
483 ndmta_mover_apply_pending (sess);
484 did_something++;
485 }
486 return did_something;
487 }
488
489 if (n_read > ta->mover_state.bytes_left_to_read)
490 n_read = ta->mover_state.bytes_left_to_read;
491
492 if (n_read < count) {
493 /* Active, but paused awaiting MOVER_READ request */
494 return did_something; /* mover blocked */
495 }
496
497 if (ta->mover_want_pos < ta->mover_state.window_offset
498 || ta->mover_want_pos >= ta->mover_window_end) {
499 ndmta_mover_pause_pending (sess, NDMP9_MOVER_PAUSE_SEEK);
500 goto again;
501 }
502
503 max_read = ta->mover_window_end - ta->mover_want_pos;
504 if (n_read > max_read)
505 n_read = max_read;
506
507 want_window_off = ta->mover_want_pos - ta->mover_state.window_offset;
508
509 /* make an estimate of the block size - the tape agent's block size, or
510 * if it's in variable block size mode, the mover's record size: "When
511 * in variable block mode, as indicated by a tape block_size value of
512 * zero, the mover record size defines the actual block size used by
513 * the tape subsystem." (NDMPv4 RFC, Section 3.6.2.1) */
514 block_size = ta->tape_state.block_size.value;
515 if (!block_size)
516 block_size = ta->mover_state.record_size;
517
518 want_blockno = ta->mover_window_first_blockno + want_window_off / block_size;
519
520 if (ta->tb_blockno != want_blockno) {
521 unsigned long xsr_count, xsr_resid;
522
523 ndmos_tape_sync_state(sess);
524 cur_blockno = ta->tape_state.blockno.value;
525 if (cur_blockno < want_blockno) {
526 xsr_count = want_blockno - cur_blockno;
527 error = ndmos_tape_mtio (sess, NDMP9_MTIO_FSR,
528 xsr_count, &xsr_resid);
529 if (error == NDMP9_EOF_ERR) {
530 ndmta_mover_pause_pending (sess,
531 NDMP9_MOVER_PAUSE_EOF);
532 goto again;
533 }
534 if (error != NDMP9_NO_ERR) {
535 ndmta_mover_halt_pending (sess,
536 NDMP9_MOVER_HALT_MEDIA_ERROR);
537 goto again;
538 }
539 if (xsr_resid > 0) {
540 ndmta_mover_pause_pending (sess,
541 NDMP9_MOVER_PAUSE_EOF);
542 goto again;
543 }
544 } else if (cur_blockno > want_blockno) {
545 xsr_count = cur_blockno - want_blockno;
546 error = ndmos_tape_mtio (sess, NDMP9_MTIO_BSR,
547 xsr_count, &xsr_resid);
548 if (error != NDMP9_NO_ERR || xsr_resid > 0) {
549 ndmta_mover_halt_pending (sess,
550 NDMP9_MOVER_HALT_MEDIA_ERROR);
551 goto again;
552 }
553 } else {
554 /* in position */
555 }
556
557 data = ta->tape_buffer;
558 done_count = 0;
559 error = ndmos_tape_read (sess, data, count, &done_count);
560 did_something++;
561
562 if (error == NDMP9_EOF_ERR) {
563 ndmta_mover_pause_pending (sess,
564 NDMP9_MOVER_PAUSE_EOF);
565 goto again;
566 }
567 /* N.B. - handling of done_count = 0 here is hacked to support
568 * non-blocking writes to a socket in amndmjob */
569 if (error != NDMP9_NO_ERR) {
570 ndmta_mover_halt_pending (sess,
571 NDMP9_MOVER_HALT_MEDIA_ERROR);
572 goto again;
573 }
574 if (done_count == 0) {
575 return did_something - 1;
576 }
577 if (done_count != count) {
578 n_read = done_count;
579 goto again;
580 }
581 ta->tb_blockno = want_blockno;
582 /* re-calcluate this, since record_size may be > block_size, in which
583 * case the record_num may not change for each block read from tape */
584 ta->mover_state.record_num = ta->mover_want_pos / ta->mover_state.record_size;
585 }
586
587 record_off = ta->mover_want_pos % ta->mover_state.record_size;
588
589 n_avail = ta->mover_state.record_size - record_off;
590 if (n_read > n_avail)
591 n_read = n_avail;
592 if (n_read != done_count) {
593 dbprintf("lost %lu bytes %lu %u\n", done_count - n_read, done_count, n_read);
594 n_read = done_count;
595 }
596
597 data = &ta->tape_buffer[record_off];
598
599 bcopy (data, ch->data + ch->end_ix, n_read);
600 ch->end_ix += n_read;
601 ta->mover_state.bytes_moved += n_read;
602 ta->mover_want_pos += n_read;
603 ta->mover_state.bytes_left_to_read -= n_read;
604
605 did_something++;
606
607 goto again; /* do as much as possible */
608 }
609
610 void
ndmta_mover_send_notice(struct ndm_session * sess)611 ndmta_mover_send_notice (struct ndm_session *sess)
612 {
613 struct ndm_tape_agent * ta = &sess->tape_acb;
614
615 if (!ta->mover_notify_pending)
616 return;
617
618 ta->mover_notify_pending = 0;
619
620 switch (ta->mover_state.state) {
621 case NDMP9_MOVER_STATE_HALTED:
622 ndma_notify_mover_halted (sess);
623 break;
624
625 case NDMP9_MOVER_STATE_PAUSED:
626 ndma_notify_mover_paused (sess);
627 break;
628
629 default:
630 /* Hmm. Why are we here. Race? */
631 break;
632 }
633 }
634
635 #endif /* !NDMOS_OPTION_NO_TAPE_AGENT */
636