xref: /original-bsd/sbin/dump/tape.c (revision 0eaa7944)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char sccsid[] = "@(#)tape.c	5.1 (Berkeley) 06/05/85";
9 #endif not lint
10 
11 #include "dump.h"
12 #include <signal.h>
13 
14 char	(*tblock)[TP_BSIZE];	/* Pointer to malloc()ed buffer for tape */
15 int	writesize;		/* Size of malloc()ed buffer for tape */
16 int	trecno = 0;
17 extern	int ntrec;		/* blocking factor on tape */
18 
19 /*
20  * Streaming dump mods (Caltech) - disk block reading and tape writing
21  * are exported to several slave processes.  While one slave writes the
22  * tape, the others read disk blocks; they pass control of the tape in
23  * a ring via pipes.  The parent process traverses the filesystem and
24  * sends daddr's, inode records, etc, through pipes to each slave.
25  * Speed from Eagle to TU77 on VAX/780 is about 140 Kbytes/second.
26  * #ifdef RDUMP version is CPU-limited to about 40 Kbytes/second.
27  */
28 struct req {			/* instruction packets sent to slaves */
29 	daddr_t dblk;
30 	int count;
31 } *req;
32 int reqsiz;
33 
34 #define SLAVES 3		/* 2 slaves read disk while 3rd writes tape */
35 #define LAG 2			/* Write behind by LAG tape blocks (rdump) */
36 int slavefd[SLAVES];		/* Pipes from master to each slave */
37 int rotor;			/* Current slave number */
38 int master;			/* Pid of master, for sending error signals */
39 int trace = 0;			/* Protocol trace; easily patchable with adb */
40 #define  tmsg	if (trace) msg
41 
42 #ifdef RDUMP
43 extern int rmtape;
44 #endif
45 
46 /*
47  * Allocate tape buffer contiguous with the array of instruction packets,
48  * so they can be written with a single write call in flusht().
49  */
50 alloctape()
51 {
52 
53 	writesize = ntrec * TP_BSIZE;
54 	reqsiz = ntrec * sizeof(struct req);
55 	req = (struct req *)malloc(reqsiz+writesize);	/* array of packets */
56 	tblock = (char (*)[TP_BSIZE]) &req[ntrec];	/* Tape buffer */
57 	return (req != NULL);
58 }
59 
60 /*
61  * Send special record to be put on tape
62  */
63 taprec(dp)
64 	char *dp;
65 {
66 
67 	tmsg("taprec %d\n", trecno);
68 	req[trecno].dblk = (daddr_t)0;
69 	req[trecno].count = 1;
70 	*(union u_spcl *)(*tblock++) = *(union u_spcl *)dp;
71 	spcl.c_tapea++;
72 	if (++trecno >= ntrec)
73 		flusht();
74 }
75 
76 dmpblk(blkno, size)
77 	daddr_t blkno;
78 	int size;
79 {
80 	int tpblks, dblkno;
81 	register int avail;
82 
83 	if (size % TP_BSIZE != 0)
84 		msg("bad size to dmpblk: %d\n", size);
85 	dblkno = fsbtodb(sblock, blkno);
86 	tpblks = size / TP_BSIZE;
87 	while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
88 		tmsg("dmpblk %d\n", avail);
89 		req[trecno].dblk = dblkno;
90 		req[trecno].count = avail;
91 		trecno += avail;
92 		spcl.c_tapea += avail;
93 		if (trecno >= ntrec)
94 			flusht();
95 		dblkno += avail * (TP_BSIZE / DEV_BSIZE);
96 		tpblks -= avail;
97 	}
98 }
99 
100 int	nogripe = 0;
101 
102 tperror() {
103 	if (pipeout) {
104 		msg("Tape write error on %s\n", tape);
105 		msg("Cannot recover\n");
106 		dumpabort();
107 		/* NOTREACHED */
108 	}
109 	msg("Tape write error on tape %d\n", tapeno);
110 	broadcast("TAPE ERROR!\n");
111 	if (!query("Do you want to restart?"))
112 		dumpabort();
113 	msg("This tape will rewind.  After it is rewound,\n");
114 	msg("replace the faulty tape with a new one;\n");
115 	msg("this dump volume will be rewritten.\n");
116 	nogripe = 1;
117 	close_rewind();
118 	Exit(X_REWRITE);
119 }
120 
121 senderr()
122 {
123 
124 	perror("  DUMP: pipe error in command to slave");
125 	dumpabort();
126 }
127 
128 #ifdef RDUMP
129 tflush(cnt)
130 	int cnt;
131 {
132 	int i;
133 
134 	for (i = 0; i < ntrec; i++)
135 		spclrec();
136 }
137 #endif RDUMP
138 
139 flusht()
140 {
141 	int sig, siz = (char *)tblock - (char *)req;
142 
143 	tmsg("flusht %d\n", siz);
144 	sig = sigblock(1<<SIGINT-1 | 1<<SIGIOT-1);  /* Don't interrupt write */
145 	if (write(slavefd[rotor], req, siz) != siz)
146 		senderr();
147 	sigsetmask(sig);
148 	if (++rotor >= SLAVES) rotor = 0;
149 	tblock = (char (*)[TP_BSIZE]) &req[ntrec];
150 	trecno = 0;
151 	asize += writesize/density;
152 	asize += 7;			/* inter-record gap (why fixed?) */
153 	blockswritten += ntrec;
154 	if (!pipeout && asize > tsize) {
155 		close_rewind();
156 		otape();
157 	}
158 	timeest();
159 }
160 
161 rewind()
162 {
163 	register int f;
164 
165 	if (pipeout)
166 		return;
167 	for (f = 0; f < SLAVES; f++)
168 		close(slavefd[f]);
169 	while (wait(NULL) >= 0)    ;	/* wait for any signals from slaves */
170 	msg("Tape rewinding\n");
171 #ifdef RDUMP
172 	rmtclose();
173 	while (rmtopen(tape, 0) < 0)
174 		sleep(10);
175 	rmtclose();
176 #else
177 	close(to);
178 	while ((f = open(tape, 0)) < 0)
179 		sleep (10);
180 	close(f);
181 #endif
182 }
183 
184 close_rewind()
185 {
186 	rewind();
187 	if (!nogripe) {
188 		msg("Change Tapes: Mount tape #%d\n", tapeno+1);
189 		broadcast("CHANGE TAPES!\7\7\n");
190 	}
191 	while (!query("Is the new tape mounted and ready to go?"))
192 		if (query("Do you want to abort?"))
193 			dumpabort();
194 }
195 
196 /*
197  *	We implement taking and restoring checkpoints on the tape level.
198  *	When each tape is opened, a new process is created by forking; this
199  *	saves all of the necessary context in the parent.  The child
200  *	continues the dump; the parent waits around, saving the context.
201  *	If the child returns X_REWRITE, then it had problems writing that tape;
202  *	this causes the parent to fork again, duplicating the context, and
203  *	everything continues as if nothing had happened.
204  */
205 
206 otape()
207 {
208 	int	parentpid;
209 	int	childpid;
210 	int	status;
211 	int	waitpid;
212 	int	interrupt();
213 
214 	parentpid = getpid();
215 
216     restore_check_point:
217 	signal(SIGINT, interrupt);
218 	/*
219 	 *	All signals are inherited...
220 	 */
221 	childpid = fork();
222 	if (childpid < 0) {
223 		msg("Context save fork fails in parent %d\n", parentpid);
224 		Exit(X_ABORT);
225 	}
226 	if (childpid != 0) {
227 		/*
228 		 *	PARENT:
229 		 *	save the context by waiting
230 		 *	until the child doing all of the work returns.
231 		 *	don't catch the interrupt
232 		 */
233 		signal(SIGINT, SIG_IGN);
234 #ifdef TDEBUG
235 		msg("Tape: %d; parent process: %d child process %d\n",
236 			tapeno+1, parentpid, childpid);
237 #endif TDEBUG
238 		while ((waitpid = wait(&status)) != childpid)
239 			msg("Parent %d waiting for child %d has another child %d return\n",
240 				parentpid, childpid, waitpid);
241 		if (status & 0xFF) {
242 			msg("Child %d returns LOB status %o\n",
243 				childpid, status&0xFF);
244 		}
245 		status = (status >> 8) & 0xFF;
246 #ifdef TDEBUG
247 		switch(status) {
248 			case X_FINOK:
249 				msg("Child %d finishes X_FINOK\n", childpid);
250 				break;
251 			case X_ABORT:
252 				msg("Child %d finishes X_ABORT\n", childpid);
253 				break;
254 			case X_REWRITE:
255 				msg("Child %d finishes X_REWRITE\n", childpid);
256 				break;
257 			default:
258 				msg("Child %d finishes unknown %d\n",
259 				    childpid, status);
260 				break;
261 		}
262 #endif TDEBUG
263 		switch(status) {
264 			case X_FINOK:
265 				Exit(X_FINOK);
266 			case X_ABORT:
267 				Exit(X_ABORT);
268 			case X_REWRITE:
269 				goto restore_check_point;
270 			default:
271 				msg("Bad return code from dump: %d\n", status);
272 				Exit(X_ABORT);
273 		}
274 		/*NOTREACHED*/
275 	} else {	/* we are the child; just continue */
276 #ifdef TDEBUG
277 		sleep(4);	/* allow time for parent's message to get out */
278 		msg("Child on Tape %d has parent %d, my pid = %d\n",
279 			tapeno+1, parentpid, getpid());
280 #endif
281 #ifdef RDUMP
282 		while ((to = rmtopen(tape, 2)) < 0)
283 #else
284 		while ((to = pipeout ? 1 : creat(tape, 0666)) < 0)
285 #endif
286 			if (!query("Cannot open tape.  Do you want to retry the open?"))
287 				dumpabort();
288 
289 		enslave();  /* Share open tape file descriptor with slaves */
290 
291 		asize = 0;
292 		tapeno++;		/* current tape sequence */
293 		newtape++;		/* new tape signal */
294 		spcl.c_volume++;
295 		spcl.c_type = TS_TAPE;
296 		spclrec();
297 		if (tapeno > 1)
298 			msg("Tape %d begins with blocks from ino %d\n",
299 				tapeno, ino);
300 	}
301 }
302 
303 dumpabort()
304 {
305 	if (master != 0 && master != getpid())
306 		kill(master, SIGIOT);
307 	msg("The ENTIRE dump is aborted.\n");
308 	Exit(X_ABORT);
309 }
310 
311 Exit(status)
312 {
313 #ifdef TDEBUG
314 	msg("pid = %d exits with status %d\n", getpid(), status);
315 #endif TDEBUG
316 	exit(status);
317 }
318 
319 #define OK 020
320 char tok = OK;
321 
322 enslave()
323 {
324 	int prev[2], next[2], cmd[2];	/* file descriptors for pipes */
325 	int i, j, ret, slavepid;
326 
327 	master = getpid();
328 	signal(SIGPIPE, dumpabort);
329 	signal(SIGIOT, tperror); /* SIGIOT asks for restart from checkpoint */
330 	pipe(prev);
331 	for (i = rotor = 0; i < SLAVES; ++i) {
332 		if ((i < SLAVES - 1 && pipe(next) < 0) || pipe(cmd) < 0
333 				|| (slavepid = fork()) < 0) {
334 			perror("  DUMP: too many slaves");
335 			dumpabort();
336 		}
337 		if (i >= SLAVES - 1)
338 			next[1] = prev[1];	    /* Last slave loops back */
339 		slavefd[i] = cmd[1];
340 		if (slavepid == 0) {		    /* Slave starts up here */
341 			for (j = 0; j <= i; j++)
342 				close(slavefd[j]);
343 			if (i < SLAVES - 1) {
344 				close(prev[1]);
345 				close(next[0]);
346 			} else {		    /* Insert initial token */
347 				if ((ret = write(next[1], &tok, 1)) != 1)
348 					ringerr(ret, "cannot start token");
349 			}
350 			doslave(i, cmd[0], prev[0], next[1]);
351 			close(next[1]);
352 			j = read(prev[0], &tok, 1);   /* Eat the final token */
353 #ifdef RDUMP				    /* Read remaining acknowledges */
354 			for (; j > 0 && (tok &~ OK) > 0; tok--) {
355 				if (rmtwrite2() != writesize && (tok & OK)) {
356 					kill(master, SIGIOT);
357 					tok &= ~OK;
358 				}
359 			}
360 #endif
361 			Exit(X_FINOK);
362 		}
363 		close(cmd[0]);
364 		close(next[1]);
365 		close(prev[0]);
366 		prev[0] = next[0];
367 	}
368 	master = 0;
369 }
370 
371 /*
372  * Somebody must have died, should never happen
373  */
374 ringerr(code, msg, a1, a2)
375 	int code;
376 	char *msg;
377 	int a1, a2;
378 {
379 	char buf[BUFSIZ];
380 
381 	fprintf(stderr, "  DUMP: ");
382 	sprintf(buf, msg, a1, a2);
383 	if (code < 0)
384 		perror(msg);
385 	else if (code == 0)
386 		fprintf(stderr, "%s: unexpected EOF\n", buf);
387 	else
388 		fprintf(stderr, "%s: code %d\n", buf, code);
389 	kill(master, SIGPIPE);
390 	Exit(X_ABORT);
391 }
392 
393 int childnum;
394 sigpipe()
395 {
396 
397 	ringerr(childnum, "SIGPIPE raised");
398 }
399 
400 doslave(num, cmd, prev, next)
401 	int num, cmd, prev, next;
402 {
403 	int ret;
404 
405 	tmsg("slave %d\n", num);
406 	signal(SIGINT, SIG_IGN); 		/* Master handles it */
407 	signal(SIGTERM, SIG_IGN);
408 	signal(SIGPIPE, sigpipe);
409 	childnum = num;
410 	close(fi);
411 	if ((fi = open(disk, 0)) < 0) {		/* Need our own seek pointer */
412 		perror("  DUMP: can't reopen disk");
413 		kill(master, SIGPIPE);
414 		Exit(X_ABORT);
415 	}
416 	while ((ret = readpipe(cmd, req, reqsiz)) == reqsiz) {
417 		register struct req *p = req;
418 		for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) {
419 			if (p->dblk) {
420 				tmsg("%d READS %d\n", num, p->count);
421 				bread(p->dblk, tblock[trecno],
422 				    p->count * TP_BSIZE);
423 			} else {
424 				tmsg("%d PIPEIN %d\n", num, p->count);
425 				if (p->count != 1)
426 					ringerr(11, "%d PIPEIN %d", num,
427 						p->count);
428 				if (readpipe(cmd, tblock[trecno], TP_BSIZE) != TP_BSIZE)
429 					senderr();
430 			}
431 		}
432 		if ((ret = read(prev, &tok, 1)) != 1)
433 			ringerr(ret, "read token");	/* Wait your turn */
434 		tmsg("%d WRITE\n", num);
435 #ifdef RDUMP
436 		if (tok & OK) {
437 			rmtwrite0(writesize);
438 			rmtwrite1(tblock[0], writesize);
439 			tok++;		/* Number of writes in progress */
440 		}
441 		if (tok > (LAG|OK) && (--tok, rmtwrite2() != writesize)) {
442 #else
443 		if ((tok & OK) &&
444 		    write(to, tblock[0], writesize) != writesize) {
445 			perror(tape);
446 #endif
447 			kill(master, SIGIOT);	/* restart from checkpoint */
448 			tok &= ~OK;
449 		}
450 		if ((ret = write(next, &tok, 1)) != 1)
451 			ringerr(ret, "write token"); /* Next slave's turn */
452 	}
453 	if (ret != 0)
454 		ringerr(ret, "partial record?");
455 	tmsg("%d CLOSE\n", num);
456 }
457 
458 /*
459  * Since a read from a pipe may not return all we asked for
460  * we must loop until we get all we need
461  */
462 readpipe(fd, buf, cnt)
463 	int fd;
464 	char *buf;
465 	int cnt;
466 {
467 	int rd, got;
468 
469 	for (rd = cnt; rd > 0; rd -= got) {
470 		got = read(fd, buf, rd);
471 		if (got < 0)
472 			return (got);
473 		if (got == 0)
474 			return (cnt - rd);
475 		buf += got;
476 	}
477 	return (cnt);
478 }
479