xref: /csrg-svn/sbin/dump/tape.c (revision 18012)
117527Ssam #ifndef lint
2*18012Smckusick static	char *sccsid = "@(#)tape.c	1.9 (Berkeley) 02/19/85";
317527Ssam #endif
417527Ssam 
51425Sroot #include "dump.h"
6*18012Smckusick #include <signal.h>
71425Sroot 
810911Ssam char	(*tblock)[TP_BSIZE];	/* Pointer to malloc()ed buffer for tape */
910911Ssam int	writesize;		/* Size of malloc()ed buffer for tape */
104774Smckusic int	trecno = 0;
11*18012Smckusick extern	int ntrec;		/* blocking factor on tape */
121425Sroot 
1310911Ssam /*
14*18012Smckusick  * Streaming dump mods (Caltech) - disk block reading and tape writing
15*18012Smckusick  * are exported to several slave processes.  While one slave writes the
16*18012Smckusick  * tape, the others read disk blocks; they pass control of the tape in
17*18012Smckusick  * a ring via pipes.  The parent process traverses the filesystem and
18*18012Smckusick  * sends daddr's, inode records, etc, through pipes to each slave.
19*18012Smckusick  * Speed from Eagle to TU77 on VAX/780 is about 140 Kbytes/second.
20*18012Smckusick  * #ifdef RDUMP version is CPU-limited to about 40 Kbytes/second.
2110911Ssam  */
22*18012Smckusick struct req {			/* instruction packets sent to slaves */
23*18012Smckusick 	daddr_t dblk;
24*18012Smckusick 	int count;
25*18012Smckusick } *req;
26*18012Smckusick int reqsiz;
27*18012Smckusick 
28*18012Smckusick #define SLAVES 3		/* 2 slaves read disk while 3rd writes tape */
29*18012Smckusick #define LAG 2			/* Write behind by LAG tape blocks (rdump) */
30*18012Smckusick int slavefd[SLAVES];		/* Pipes from master to each slave */
31*18012Smckusick int rotor;			/* Current slave number */
32*18012Smckusick int master;			/* Pid of master, for sending error signals */
33*18012Smckusick int trace = 0;			/* Protocol trace; easily patchable with adb */
34*18012Smckusick #define  tmsg	if (trace) msg
35*18012Smckusick 
36*18012Smckusick #ifdef RDUMP
37*18012Smckusick extern int rmtape;
38*18012Smckusick #endif
39*18012Smckusick 
40*18012Smckusick /*
41*18012Smckusick  * Allocate tape buffer contiguous with the array of instruction packets,
42*18012Smckusick  * so they can be written with a single write call in flusht().
43*18012Smckusick  */
4410911Ssam alloctape()
4510911Ssam {
4610911Ssam 
4710911Ssam 	writesize = ntrec * TP_BSIZE;
48*18012Smckusick 	reqsiz = ntrec * sizeof(struct req);
49*18012Smckusick 	req = (struct req *)malloc(reqsiz+writesize);	/* array of packets */
50*18012Smckusick 	tblock = (char (*)[TP_BSIZE]) &req[ntrec];	/* Tape buffer */
51*18012Smckusick 	return (req != NULL);
5210911Ssam }
5310911Ssam 
54*18012Smckusick /*
55*18012Smckusick  * Send special record to be put on tape
56*18012Smckusick  */
571425Sroot taprec(dp)
585329Smckusic 	char *dp;
591425Sroot {
601425Sroot 
61*18012Smckusick 	tmsg("taprec %d\n", trecno);
62*18012Smckusick 	req[trecno].dblk = (daddr_t)0;
63*18012Smckusick 	req[trecno].count = 1;
64*18012Smckusick 	*(union u_spcl *)(*tblock++) = *(union u_spcl *)dp;
651425Sroot 	spcl.c_tapea++;
66*18012Smckusick 	if (++trecno >= ntrec)
671425Sroot 		flusht();
681425Sroot }
691425Sroot 
704774Smckusic dmpblk(blkno, size)
714774Smckusic 	daddr_t blkno;
724774Smckusic 	int size;
731425Sroot {
74*18012Smckusick 	int tpblks, dblkno;
75*18012Smckusick 	register int avail;
761425Sroot 
775329Smckusic 	if (size % TP_BSIZE != 0)
784774Smckusic 		msg("bad size to dmpblk: %d\n", size);
795329Smckusic 	dblkno = fsbtodb(sblock, blkno);
80*18012Smckusick 	tpblks = size / TP_BSIZE;
81*18012Smckusick 	while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
82*18012Smckusick 		tmsg("dmpblk %d\n", avail);
83*18012Smckusick 		req[trecno].dblk = dblkno;
84*18012Smckusick 		req[trecno].count = avail;
854774Smckusic 		trecno += avail;
864774Smckusic 		spcl.c_tapea += avail;
87*18012Smckusick 		if (trecno >= ntrec)
88*18012Smckusick 			flusht();
895329Smckusic 		dblkno += avail * (TP_BSIZE / DEV_BSIZE);
905329Smckusic 		tpblks -= avail;
914774Smckusic 	}
921425Sroot }
931425Sroot 
941425Sroot int	nogripe = 0;
951425Sroot 
96*18012Smckusick tperror() {
97*18012Smckusick 	if (pipeout) {
98*18012Smckusick 		msg("Tape write error on %s\n", tape);
99*18012Smckusick 		msg("Cannot recover\n");
100*18012Smckusick 		dumpabort();
101*18012Smckusick 		/* NOTREACHED */
102*18012Smckusick 	}
103*18012Smckusick 	msg("Tape write error on tape %d\n", tapeno);
104*18012Smckusick 	broadcast("TAPE ERROR!\n");
105*18012Smckusick 	if (!query("Do you want to restart?"))
106*18012Smckusick 		dumpabort();
107*18012Smckusick 	msg("This tape will rewind.  After it is rewound,\n");
108*18012Smckusick 	msg("replace the faulty tape with a new one;\n");
109*18012Smckusick 	msg("this dump volume will be rewritten.\n");
110*18012Smckusick 	nogripe = 1;
111*18012Smckusick 	close_rewind();
112*18012Smckusick 	Exit(X_REWRITE);
113*18012Smckusick }
114*18012Smckusick 
115*18012Smckusick senderr()
116*18012Smckusick {
117*18012Smckusick 
118*18012Smckusick 	perror("dump: pipe error in command to slave");
119*18012Smckusick 	dumpabort();
120*18012Smckusick }
121*18012Smckusick 
122*18012Smckusick #ifdef RDUMP
123*18012Smckusick tflush(cnt)
124*18012Smckusick 	int cnt;
125*18012Smckusick {
126*18012Smckusick 	int i;
127*18012Smckusick 
128*18012Smckusick 	for (i = 0; i < ntrec; i++)
129*18012Smckusick 		spclrec();
130*18012Smckusick }
131*18012Smckusick #endif RDUMP
132*18012Smckusick 
1331425Sroot flusht()
1341425Sroot {
135*18012Smckusick 	int sig, siz = (char *)tblock - (char *)req;
1361425Sroot 
137*18012Smckusick 	tmsg("flusht %d\n", siz);
138*18012Smckusick 	sig = sigblock(1<<SIGINT-1 | 1<<SIGIOT-1);  /* Don't interrupt write */
139*18012Smckusick 	if (write(slavefd[rotor], req, siz) != siz)
140*18012Smckusick 		senderr();
141*18012Smckusick 	sigsetmask(sig);
142*18012Smckusick 	if (++rotor >= SLAVES) rotor = 0;
143*18012Smckusick 	tblock = (char (*)[TP_BSIZE]) &req[ntrec];
1441425Sroot 	trecno = 0;
14510911Ssam 	asize += writesize/density;
146*18012Smckusick 	asize += 7;			/* inter-record gap (why fixed?) */
14710911Ssam 	blockswritten += ntrec;
14812331Smckusick 	if (!pipeout && asize > tsize) {
1491425Sroot 		close_rewind();
1501425Sroot 		otape();
1511425Sroot 	}
1521425Sroot 	timeest();
1531425Sroot }
1541425Sroot 
1551425Sroot rewind()
1561425Sroot {
157*18012Smckusick 	register int f;
15812331Smckusick 
15912331Smckusick 	if (pipeout)
16012331Smckusick 		return;
161*18012Smckusick 	for (f = 0; f < SLAVES; f++)
162*18012Smckusick 		close(slavefd[f]);
163*18012Smckusick 	while (wait(NULL) >= 0)    ;	/* wait for any signals from slaves */
164*18012Smckusick 	msg("Tape rewinding\n");
165*18012Smckusick #ifdef RDUMP
166*18012Smckusick 	rmtclose();
167*18012Smckusick 	while (rmtopen(tape, 0) < 0)
168*18012Smckusick 		sleep(10);
169*18012Smckusick 	rmtclose();
1701425Sroot #else
1713214Swnj 	close(to);
1723214Swnj 	while ((f = open(tape, 0)) < 0)
1733214Swnj 		sleep (10);
1743214Swnj 	close(f);
1751425Sroot #endif
1761425Sroot }
1771425Sroot 
1781425Sroot close_rewind()
1791425Sroot {
180*18012Smckusick 	rewind();
181*18012Smckusick 	if (!nogripe) {
1821425Sroot 		msg("Change Tapes: Mount tape #%d\n", tapeno+1);
1831425Sroot 		broadcast("CHANGE TAPES!\7\7\n");
1841425Sroot 	}
185*18012Smckusick 	while (!query("Is the new tape mounted and ready to go?"))
186*18012Smckusick 		if (query("Do you want to abort?"))
1871425Sroot 			dumpabort();
1881425Sroot }
1891425Sroot 
1901425Sroot /*
191*18012Smckusick  *	We implement taking and restoring checkpoints on the tape level.
1921425Sroot  *	When each tape is opened, a new process is created by forking; this
1931425Sroot  *	saves all of the necessary context in the parent.  The child
1941425Sroot  *	continues the dump; the parent waits around, saving the context.
1951425Sroot  *	If the child returns X_REWRITE, then it had problems writing that tape;
1961425Sroot  *	this causes the parent to fork again, duplicating the context, and
1971425Sroot  *	everything continues as if nothing had happened.
1981425Sroot  */
1991425Sroot 
2001425Sroot otape()
2011425Sroot {
2021425Sroot 	int	parentpid;
2031425Sroot 	int	childpid;
2041425Sroot 	int	status;
2051425Sroot 	int	waitpid;
2061425Sroot 	int	interrupt();
2071425Sroot 
2081425Sroot 	parentpid = getpid();
2091425Sroot 
2101425Sroot     restore_check_point:
2111425Sroot 	signal(SIGINT, interrupt);
2121425Sroot 	/*
2131425Sroot 	 *	All signals are inherited...
2141425Sroot 	 */
2151425Sroot 	childpid = fork();
216*18012Smckusick 	if (childpid < 0) {
2171425Sroot 		msg("Context save fork fails in parent %d\n", parentpid);
2181425Sroot 		Exit(X_ABORT);
2191425Sroot 	}
220*18012Smckusick 	if (childpid != 0) {
2211425Sroot 		/*
2221425Sroot 		 *	PARENT:
2231425Sroot 		 *	save the context by waiting
2241425Sroot 		 *	until the child doing all of the work returns.
225*18012Smckusick 		 *	don't catch the interrupt
2261425Sroot 		 */
2271425Sroot 		signal(SIGINT, SIG_IGN);
2281425Sroot #ifdef TDEBUG
2291425Sroot 		msg("Tape: %d; parent process: %d child process %d\n",
2301425Sroot 			tapeno+1, parentpid, childpid);
2311425Sroot #endif TDEBUG
232*18012Smckusick 		while ((waitpid = wait(&status)) != childpid)
233*18012Smckusick 			msg("Parent %d waiting for child %d has another child %d return\n",
234*18012Smckusick 				parentpid, childpid, waitpid);
235*18012Smckusick 		if (status & 0xFF) {
2361425Sroot 			msg("Child %d returns LOB status %o\n",
2371425Sroot 				childpid, status&0xFF);
2381425Sroot 		}
2391425Sroot 		status = (status >> 8) & 0xFF;
2401425Sroot #ifdef TDEBUG
241*18012Smckusick 		switch(status) {
2421425Sroot 			case X_FINOK:
2431425Sroot 				msg("Child %d finishes X_FINOK\n", childpid);
2441425Sroot 				break;
2451425Sroot 			case X_ABORT:
2461425Sroot 				msg("Child %d finishes X_ABORT\n", childpid);
2471425Sroot 				break;
2481425Sroot 			case X_REWRITE:
2491425Sroot 				msg("Child %d finishes X_REWRITE\n", childpid);
2501425Sroot 				break;
2511425Sroot 			default:
252*18012Smckusick 				msg("Child %d finishes unknown %d\n",
253*18012Smckusick 				    childpid, status);
2541425Sroot 				break;
2551425Sroot 		}
2561425Sroot #endif TDEBUG
257*18012Smckusick 		switch(status) {
2581425Sroot 			case X_FINOK:
2591425Sroot 				Exit(X_FINOK);
2601425Sroot 			case X_ABORT:
2611425Sroot 				Exit(X_ABORT);
2621425Sroot 			case X_REWRITE:
2631425Sroot 				goto restore_check_point;
2641425Sroot 			default:
2651425Sroot 				msg("Bad return code from dump: %d\n", status);
2661425Sroot 				Exit(X_ABORT);
2671425Sroot 		}
2681425Sroot 		/*NOTREACHED*/
2691425Sroot 	} else {	/* we are the child; just continue */
2701425Sroot #ifdef TDEBUG
2711425Sroot 		sleep(4);	/* allow time for parent's message to get out */
2721425Sroot 		msg("Child on Tape %d has parent %d, my pid = %d\n",
2731425Sroot 			tapeno+1, parentpid, getpid());
2741425Sroot #endif
275*18012Smckusick #ifdef RDUMP
276*18012Smckusick 		while ((to = rmtopen(tape, 2)) < 0)
277*18012Smckusick #else
278*18012Smckusick 		while ((to = pipeout ? 1 : creat(tape, 0666)) < 0)
279*18012Smckusick #endif
280*18012Smckusick 			if (!query("Cannot open tape.  Do you want to retry the open?"))
281*18012Smckusick 				dumpabort();
2821425Sroot 
283*18012Smckusick 		enslave();  /* Share open tape file descriptor with slaves */
284*18012Smckusick 
2851425Sroot 		asize = 0;
2861425Sroot 		tapeno++;		/* current tape sequence */
2871425Sroot 		newtape++;		/* new tape signal */
2881425Sroot 		spcl.c_volume++;
2891425Sroot 		spcl.c_type = TS_TAPE;
2901425Sroot 		spclrec();
2911425Sroot 		if (tapeno > 1)
2921425Sroot 			msg("Tape %d begins with blocks from ino %d\n",
2931425Sroot 				tapeno, ino);
2941425Sroot 	}
2951425Sroot }
2961425Sroot 
2971425Sroot dumpabort()
2981425Sroot {
299*18012Smckusick 	if (master != 0 && master != getpid())
300*18012Smckusick 		kill(master, SIGIOT);
3011925Swnj 	msg("The ENTIRE dump is aborted.\n");
3021425Sroot 	Exit(X_ABORT);
3031425Sroot }
3041425Sroot 
3051425Sroot Exit(status)
3061425Sroot {
3071425Sroot #ifdef TDEBUG
3081425Sroot 	msg("pid = %d exits with status %d\n", getpid(), status);
3091425Sroot #endif TDEBUG
3101925Swnj 	exit(status);
3111425Sroot }
312*18012Smckusick 
313*18012Smckusick #define OK 020
314*18012Smckusick char tok = OK;
315*18012Smckusick 
316*18012Smckusick enslave()
317*18012Smckusick {
318*18012Smckusick 	int prev[2], next[2], cmd[2];	/* file descriptors for pipes */
319*18012Smckusick 	int i, j, slavepid;
320*18012Smckusick 
321*18012Smckusick 	master = getpid();
322*18012Smckusick 	signal(SIGPIPE, dumpabort);
323*18012Smckusick 	signal(SIGIOT, tperror); /* SIGIOT asks for restart from checkpoint */
324*18012Smckusick 	pipe(prev);
325*18012Smckusick 	for (i = rotor = 0; i < SLAVES; ++i) {
326*18012Smckusick 		if ((i < SLAVES - 1 && pipe(next) < 0) || pipe(cmd) < 0
327*18012Smckusick 				|| (slavepid = fork()) < 0) {
328*18012Smckusick 			perror("  DUMP: too many slaves");
329*18012Smckusick 			dumpabort();
330*18012Smckusick 		}
331*18012Smckusick 		if (i >= SLAVES - 1)
332*18012Smckusick 			next[1] = prev[1];	    /* Last slave loops back */
333*18012Smckusick 		slavefd[i] = cmd[1];
334*18012Smckusick 		if (slavepid == 0) {		    /* Slave starts up here */
335*18012Smckusick 			for (j = 0; j <= i; j++)
336*18012Smckusick 				close(slavefd[j]);
337*18012Smckusick 			if (i < SLAVES - 1) {
338*18012Smckusick 				close(prev[1]);
339*18012Smckusick 				close(next[0]);
340*18012Smckusick 			} else {		    /* Insert initial token */
341*18012Smckusick 				if (write(next[1], &tok, 1) != 1)
342*18012Smckusick 					ringerr();
343*18012Smckusick 			}
344*18012Smckusick 			doslave(i, cmd[0], prev[0], next[1]);
345*18012Smckusick 			close(next[1]);
346*18012Smckusick 			j = read(prev[0], &tok, 1);   /* Eat the final token */
347*18012Smckusick #ifdef RDUMP				    /* Read remaining acknowledges */
348*18012Smckusick 			for (; j > 0 && (tok &~ OK) > 0; tok--) {
349*18012Smckusick 				if (rmtwrite2() != writesize && (tok & OK)) {
350*18012Smckusick 					kill(master, SIGIOT);
351*18012Smckusick 					tok &= ~OK;
352*18012Smckusick 				}
353*18012Smckusick 			}
354*18012Smckusick #endif
355*18012Smckusick 			Exit(X_FINOK);
356*18012Smckusick 		}
357*18012Smckusick 		close(cmd[0]);
358*18012Smckusick 		close(next[1]);
359*18012Smckusick 		close(prev[0]);
360*18012Smckusick 		prev[0] = next[0];
361*18012Smckusick 	}
362*18012Smckusick 	master = 0;
363*18012Smckusick }
364*18012Smckusick 
365*18012Smckusick /*
366*18012Smckusick  * Somebody must have died, should never happen
367*18012Smckusick  */
368*18012Smckusick ringerr()
369*18012Smckusick {
370*18012Smckusick 	perror("  DUMP: token passing error");
371*18012Smckusick 	kill(master, SIGPIPE);
372*18012Smckusick 	Exit(X_ABORT);
373*18012Smckusick }
374*18012Smckusick 
375*18012Smckusick doslave(num, cmd, prev, next)
376*18012Smckusick 	int num, cmd, prev, next;
377*18012Smckusick {
378*18012Smckusick 	tmsg("slave %d\n", num);
379*18012Smckusick 	signal(SIGINT, SIG_IGN); 		/* Master handles it */
380*18012Smckusick 	signal(SIGTERM, SIG_IGN);
381*18012Smckusick 	signal(SIGPIPE, ringerr);
382*18012Smckusick 	close(fi);
383*18012Smckusick 	if ((fi = open(disk, 0)) < 0) {		/* Need our own seek pointer */
384*18012Smckusick 		perror("  DUMP: can't reopen disk");
385*18012Smckusick 		kill(master, SIGPIPE);
386*18012Smckusick 		Exit(X_ABORT);
387*18012Smckusick 	}
388*18012Smckusick 	while (read(cmd, req, reqsiz) == reqsiz) {
389*18012Smckusick 		register struct req *p = req;
390*18012Smckusick 		for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) {
391*18012Smckusick 			if (p->dblk) {
392*18012Smckusick 				tmsg("%d READS %d\n", num, p->count);
393*18012Smckusick 				bread(p->dblk, tblock[trecno],
394*18012Smckusick 				    p->count * TP_BSIZE);
395*18012Smckusick 			} else {
396*18012Smckusick 				tmsg("%d PIPEIN %d\n", num, p->count);
397*18012Smckusick 				if (p->count != 1)
398*18012Smckusick 					ringerr();
399*18012Smckusick 				if (read(cmd, tblock[trecno], TP_BSIZE) != TP_BSIZE)
400*18012Smckusick 					senderr();
401*18012Smckusick 			}
402*18012Smckusick 		}
403*18012Smckusick 		if (read(prev, &tok, 1) != 1)
404*18012Smckusick 			ringerr();	/* Wait your turn */
405*18012Smckusick 		tmsg("%d WRITE\n", num);
406*18012Smckusick #ifdef RDUMP
407*18012Smckusick 		if (tok & OK) {
408*18012Smckusick 			rmtwrite0(writesize);
409*18012Smckusick 			rmtwrite1(tblock[0], writesize);
410*18012Smckusick 			tok++;		/* Number of writes in progress */
411*18012Smckusick 		}
412*18012Smckusick 		if (tok > (LAG|OK) && (--tok, rmtwrite2() != writesize)) {
413*18012Smckusick #else
414*18012Smckusick 		if ((tok & OK) &&
415*18012Smckusick 		    write(to, tblock[0], writesize) != writesize) {
416*18012Smckusick 			perror(tape);
417*18012Smckusick #endif
418*18012Smckusick 			kill(master, SIGIOT);	/* restart from checkpoint */
419*18012Smckusick 			tok &= ~OK;
420*18012Smckusick 		}
421*18012Smckusick 		if (write(next, &tok, 1) != 1)
422*18012Smckusick 			ringerr(); /* Next slave's turn */
423*18012Smckusick 	}
424*18012Smckusick 	tmsg("%d CLOSE\n", num);
425*18012Smckusick }
426