xref: /csrg-svn/sbin/dump/tape.c (revision 22040)
1*22040Sdist /*
2*22040Sdist  * Copyright (c) 1980 Regents of the University of California.
3*22040Sdist  * All rights reserved.  The Berkeley software License Agreement
4*22040Sdist  * specifies the terms and conditions for redistribution.
5*22040Sdist  */
6*22040Sdist 
717527Ssam #ifndef lint
8*22040Sdist static char sccsid[] = "@(#)tape.c	5.1 (Berkeley) 06/05/85";
9*22040Sdist #endif not lint
1017527Ssam 
111425Sroot #include "dump.h"
1218012Smckusick #include <signal.h>
131425Sroot 
1410911Ssam char	(*tblock)[TP_BSIZE];	/* Pointer to malloc()ed buffer for tape */
1510911Ssam int	writesize;		/* Size of malloc()ed buffer for tape */
164774Smckusic int	trecno = 0;
1718012Smckusick extern	int ntrec;		/* blocking factor on tape */
181425Sroot 
1910911Ssam /*
2018012Smckusick  * Streaming dump mods (Caltech) - disk block reading and tape writing
2118012Smckusick  * are exported to several slave processes.  While one slave writes the
2218012Smckusick  * tape, the others read disk blocks; they pass control of the tape in
2318012Smckusick  * a ring via pipes.  The parent process traverses the filesystem and
2418012Smckusick  * sends daddr's, inode records, etc, through pipes to each slave.
2518012Smckusick  * Speed from Eagle to TU77 on VAX/780 is about 140 Kbytes/second.
2618012Smckusick  * #ifdef RDUMP version is CPU-limited to about 40 Kbytes/second.
2710911Ssam  */
2818012Smckusick struct req {			/* instruction packets sent to slaves */
2918012Smckusick 	daddr_t dblk;
3018012Smckusick 	int count;
3118012Smckusick } *req;
3218012Smckusick int reqsiz;
3318012Smckusick 
3418012Smckusick #define SLAVES 3		/* 2 slaves read disk while 3rd writes tape */
3518012Smckusick #define LAG 2			/* Write behind by LAG tape blocks (rdump) */
3618012Smckusick int slavefd[SLAVES];		/* Pipes from master to each slave */
3718012Smckusick int rotor;			/* Current slave number */
3818012Smckusick int master;			/* Pid of master, for sending error signals */
3918012Smckusick int trace = 0;			/* Protocol trace; easily patchable with adb */
4018012Smckusick #define  tmsg	if (trace) msg
4118012Smckusick 
4218012Smckusick #ifdef RDUMP
4318012Smckusick extern int rmtape;
4418012Smckusick #endif
4518012Smckusick 
4618012Smckusick /*
4718012Smckusick  * Allocate tape buffer contiguous with the array of instruction packets,
4818012Smckusick  * so they can be written with a single write call in flusht().
4918012Smckusick  */
5010911Ssam alloctape()
5110911Ssam {
5210911Ssam 
5310911Ssam 	writesize = ntrec * TP_BSIZE;
5418012Smckusick 	reqsiz = ntrec * sizeof(struct req);
5518012Smckusick 	req = (struct req *)malloc(reqsiz+writesize);	/* array of packets */
5618012Smckusick 	tblock = (char (*)[TP_BSIZE]) &req[ntrec];	/* Tape buffer */
5718012Smckusick 	return (req != NULL);
5810911Ssam }
5910911Ssam 
6018012Smckusick /*
6118012Smckusick  * Send special record to be put on tape
6218012Smckusick  */
631425Sroot taprec(dp)
645329Smckusic 	char *dp;
651425Sroot {
661425Sroot 
6718012Smckusick 	tmsg("taprec %d\n", trecno);
6818012Smckusick 	req[trecno].dblk = (daddr_t)0;
6918012Smckusick 	req[trecno].count = 1;
7018012Smckusick 	*(union u_spcl *)(*tblock++) = *(union u_spcl *)dp;
711425Sroot 	spcl.c_tapea++;
7218012Smckusick 	if (++trecno >= ntrec)
731425Sroot 		flusht();
741425Sroot }
751425Sroot 
764774Smckusic dmpblk(blkno, size)
774774Smckusic 	daddr_t blkno;
784774Smckusic 	int size;
791425Sroot {
8018012Smckusick 	int tpblks, dblkno;
8118012Smckusick 	register int avail;
821425Sroot 
835329Smckusic 	if (size % TP_BSIZE != 0)
844774Smckusic 		msg("bad size to dmpblk: %d\n", size);
855329Smckusic 	dblkno = fsbtodb(sblock, blkno);
8618012Smckusick 	tpblks = size / TP_BSIZE;
8718012Smckusick 	while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
8818012Smckusick 		tmsg("dmpblk %d\n", avail);
8918012Smckusick 		req[trecno].dblk = dblkno;
9018012Smckusick 		req[trecno].count = avail;
914774Smckusic 		trecno += avail;
924774Smckusic 		spcl.c_tapea += avail;
9318012Smckusick 		if (trecno >= ntrec)
9418012Smckusick 			flusht();
955329Smckusic 		dblkno += avail * (TP_BSIZE / DEV_BSIZE);
965329Smckusic 		tpblks -= avail;
974774Smckusic 	}
981425Sroot }
991425Sroot 
1001425Sroot int	nogripe = 0;
1011425Sroot 
10218012Smckusick tperror() {
10318012Smckusick 	if (pipeout) {
10418012Smckusick 		msg("Tape write error on %s\n", tape);
10518012Smckusick 		msg("Cannot recover\n");
10618012Smckusick 		dumpabort();
10718012Smckusick 		/* NOTREACHED */
10818012Smckusick 	}
10918012Smckusick 	msg("Tape write error on tape %d\n", tapeno);
11018012Smckusick 	broadcast("TAPE ERROR!\n");
11118012Smckusick 	if (!query("Do you want to restart?"))
11218012Smckusick 		dumpabort();
11318012Smckusick 	msg("This tape will rewind.  After it is rewound,\n");
11418012Smckusick 	msg("replace the faulty tape with a new one;\n");
11518012Smckusick 	msg("this dump volume will be rewritten.\n");
11618012Smckusick 	nogripe = 1;
11718012Smckusick 	close_rewind();
11818012Smckusick 	Exit(X_REWRITE);
11918012Smckusick }
12018012Smckusick 
12118012Smckusick senderr()
12218012Smckusick {
12318012Smckusick 
12419982Smckusick 	perror("  DUMP: pipe error in command to slave");
12518012Smckusick 	dumpabort();
12618012Smckusick }
12718012Smckusick 
12818012Smckusick #ifdef RDUMP
12918012Smckusick tflush(cnt)
13018012Smckusick 	int cnt;
13118012Smckusick {
13218012Smckusick 	int i;
13318012Smckusick 
13418012Smckusick 	for (i = 0; i < ntrec; i++)
13518012Smckusick 		spclrec();
13618012Smckusick }
13718012Smckusick #endif RDUMP
13818012Smckusick 
1391425Sroot flusht()
1401425Sroot {
14118012Smckusick 	int sig, siz = (char *)tblock - (char *)req;
1421425Sroot 
14318012Smckusick 	tmsg("flusht %d\n", siz);
14418012Smckusick 	sig = sigblock(1<<SIGINT-1 | 1<<SIGIOT-1);  /* Don't interrupt write */
14518012Smckusick 	if (write(slavefd[rotor], req, siz) != siz)
14618012Smckusick 		senderr();
14718012Smckusick 	sigsetmask(sig);
14818012Smckusick 	if (++rotor >= SLAVES) rotor = 0;
14918012Smckusick 	tblock = (char (*)[TP_BSIZE]) &req[ntrec];
1501425Sroot 	trecno = 0;
15110911Ssam 	asize += writesize/density;
15218012Smckusick 	asize += 7;			/* inter-record gap (why fixed?) */
15310911Ssam 	blockswritten += ntrec;
15412331Smckusick 	if (!pipeout && asize > tsize) {
1551425Sroot 		close_rewind();
1561425Sroot 		otape();
1571425Sroot 	}
1581425Sroot 	timeest();
1591425Sroot }
1601425Sroot 
1611425Sroot rewind()
1621425Sroot {
16318012Smckusick 	register int f;
16412331Smckusick 
16512331Smckusick 	if (pipeout)
16612331Smckusick 		return;
16718012Smckusick 	for (f = 0; f < SLAVES; f++)
16818012Smckusick 		close(slavefd[f]);
16918012Smckusick 	while (wait(NULL) >= 0)    ;	/* wait for any signals from slaves */
17018012Smckusick 	msg("Tape rewinding\n");
17118012Smckusick #ifdef RDUMP
17218012Smckusick 	rmtclose();
17318012Smckusick 	while (rmtopen(tape, 0) < 0)
17418012Smckusick 		sleep(10);
17518012Smckusick 	rmtclose();
1761425Sroot #else
1773214Swnj 	close(to);
1783214Swnj 	while ((f = open(tape, 0)) < 0)
1793214Swnj 		sleep (10);
1803214Swnj 	close(f);
1811425Sroot #endif
1821425Sroot }
1831425Sroot 
1841425Sroot close_rewind()
1851425Sroot {
18618012Smckusick 	rewind();
18718012Smckusick 	if (!nogripe) {
1881425Sroot 		msg("Change Tapes: Mount tape #%d\n", tapeno+1);
1891425Sroot 		broadcast("CHANGE TAPES!\7\7\n");
1901425Sroot 	}
19118012Smckusick 	while (!query("Is the new tape mounted and ready to go?"))
19218012Smckusick 		if (query("Do you want to abort?"))
1931425Sroot 			dumpabort();
1941425Sroot }
1951425Sroot 
1961425Sroot /*
19718012Smckusick  *	We implement taking and restoring checkpoints on the tape level.
1981425Sroot  *	When each tape is opened, a new process is created by forking; this
1991425Sroot  *	saves all of the necessary context in the parent.  The child
2001425Sroot  *	continues the dump; the parent waits around, saving the context.
2011425Sroot  *	If the child returns X_REWRITE, then it had problems writing that tape;
2021425Sroot  *	this causes the parent to fork again, duplicating the context, and
2031425Sroot  *	everything continues as if nothing had happened.
2041425Sroot  */
2051425Sroot 
2061425Sroot otape()
2071425Sroot {
2081425Sroot 	int	parentpid;
2091425Sroot 	int	childpid;
2101425Sroot 	int	status;
2111425Sroot 	int	waitpid;
2121425Sroot 	int	interrupt();
2131425Sroot 
2141425Sroot 	parentpid = getpid();
2151425Sroot 
2161425Sroot     restore_check_point:
2171425Sroot 	signal(SIGINT, interrupt);
2181425Sroot 	/*
2191425Sroot 	 *	All signals are inherited...
2201425Sroot 	 */
2211425Sroot 	childpid = fork();
22218012Smckusick 	if (childpid < 0) {
2231425Sroot 		msg("Context save fork fails in parent %d\n", parentpid);
2241425Sroot 		Exit(X_ABORT);
2251425Sroot 	}
22618012Smckusick 	if (childpid != 0) {
2271425Sroot 		/*
2281425Sroot 		 *	PARENT:
2291425Sroot 		 *	save the context by waiting
2301425Sroot 		 *	until the child doing all of the work returns.
23118012Smckusick 		 *	don't catch the interrupt
2321425Sroot 		 */
2331425Sroot 		signal(SIGINT, SIG_IGN);
2341425Sroot #ifdef TDEBUG
2351425Sroot 		msg("Tape: %d; parent process: %d child process %d\n",
2361425Sroot 			tapeno+1, parentpid, childpid);
2371425Sroot #endif TDEBUG
23818012Smckusick 		while ((waitpid = wait(&status)) != childpid)
23918012Smckusick 			msg("Parent %d waiting for child %d has another child %d return\n",
24018012Smckusick 				parentpid, childpid, waitpid);
24118012Smckusick 		if (status & 0xFF) {
2421425Sroot 			msg("Child %d returns LOB status %o\n",
2431425Sroot 				childpid, status&0xFF);
2441425Sroot 		}
2451425Sroot 		status = (status >> 8) & 0xFF;
2461425Sroot #ifdef TDEBUG
24718012Smckusick 		switch(status) {
2481425Sroot 			case X_FINOK:
2491425Sroot 				msg("Child %d finishes X_FINOK\n", childpid);
2501425Sroot 				break;
2511425Sroot 			case X_ABORT:
2521425Sroot 				msg("Child %d finishes X_ABORT\n", childpid);
2531425Sroot 				break;
2541425Sroot 			case X_REWRITE:
2551425Sroot 				msg("Child %d finishes X_REWRITE\n", childpid);
2561425Sroot 				break;
2571425Sroot 			default:
25818012Smckusick 				msg("Child %d finishes unknown %d\n",
25918012Smckusick 				    childpid, status);
2601425Sroot 				break;
2611425Sroot 		}
2621425Sroot #endif TDEBUG
26318012Smckusick 		switch(status) {
2641425Sroot 			case X_FINOK:
2651425Sroot 				Exit(X_FINOK);
2661425Sroot 			case X_ABORT:
2671425Sroot 				Exit(X_ABORT);
2681425Sroot 			case X_REWRITE:
2691425Sroot 				goto restore_check_point;
2701425Sroot 			default:
2711425Sroot 				msg("Bad return code from dump: %d\n", status);
2721425Sroot 				Exit(X_ABORT);
2731425Sroot 		}
2741425Sroot 		/*NOTREACHED*/
2751425Sroot 	} else {	/* we are the child; just continue */
2761425Sroot #ifdef TDEBUG
2771425Sroot 		sleep(4);	/* allow time for parent's message to get out */
2781425Sroot 		msg("Child on Tape %d has parent %d, my pid = %d\n",
2791425Sroot 			tapeno+1, parentpid, getpid());
2801425Sroot #endif
28118012Smckusick #ifdef RDUMP
28218012Smckusick 		while ((to = rmtopen(tape, 2)) < 0)
28318012Smckusick #else
28418012Smckusick 		while ((to = pipeout ? 1 : creat(tape, 0666)) < 0)
28518012Smckusick #endif
28618012Smckusick 			if (!query("Cannot open tape.  Do you want to retry the open?"))
28718012Smckusick 				dumpabort();
2881425Sroot 
28918012Smckusick 		enslave();  /* Share open tape file descriptor with slaves */
29018012Smckusick 
2911425Sroot 		asize = 0;
2921425Sroot 		tapeno++;		/* current tape sequence */
2931425Sroot 		newtape++;		/* new tape signal */
2941425Sroot 		spcl.c_volume++;
2951425Sroot 		spcl.c_type = TS_TAPE;
2961425Sroot 		spclrec();
2971425Sroot 		if (tapeno > 1)
2981425Sroot 			msg("Tape %d begins with blocks from ino %d\n",
2991425Sroot 				tapeno, ino);
3001425Sroot 	}
3011425Sroot }
3021425Sroot 
3031425Sroot dumpabort()
3041425Sroot {
30518012Smckusick 	if (master != 0 && master != getpid())
30618012Smckusick 		kill(master, SIGIOT);
3071925Swnj 	msg("The ENTIRE dump is aborted.\n");
3081425Sroot 	Exit(X_ABORT);
3091425Sroot }
3101425Sroot 
3111425Sroot Exit(status)
3121425Sroot {
3131425Sroot #ifdef TDEBUG
3141425Sroot 	msg("pid = %d exits with status %d\n", getpid(), status);
3151425Sroot #endif TDEBUG
3161925Swnj 	exit(status);
3171425Sroot }
31818012Smckusick 
31918012Smckusick #define OK 020
32018012Smckusick char tok = OK;
32118012Smckusick 
32218012Smckusick enslave()
32318012Smckusick {
32418012Smckusick 	int prev[2], next[2], cmd[2];	/* file descriptors for pipes */
32519982Smckusick 	int i, j, ret, slavepid;
32618012Smckusick 
32718012Smckusick 	master = getpid();
32818012Smckusick 	signal(SIGPIPE, dumpabort);
32918012Smckusick 	signal(SIGIOT, tperror); /* SIGIOT asks for restart from checkpoint */
33018012Smckusick 	pipe(prev);
33118012Smckusick 	for (i = rotor = 0; i < SLAVES; ++i) {
33218012Smckusick 		if ((i < SLAVES - 1 && pipe(next) < 0) || pipe(cmd) < 0
33318012Smckusick 				|| (slavepid = fork()) < 0) {
33418012Smckusick 			perror("  DUMP: too many slaves");
33518012Smckusick 			dumpabort();
33618012Smckusick 		}
33718012Smckusick 		if (i >= SLAVES - 1)
33818012Smckusick 			next[1] = prev[1];	    /* Last slave loops back */
33918012Smckusick 		slavefd[i] = cmd[1];
34018012Smckusick 		if (slavepid == 0) {		    /* Slave starts up here */
34118012Smckusick 			for (j = 0; j <= i; j++)
34218012Smckusick 				close(slavefd[j]);
34318012Smckusick 			if (i < SLAVES - 1) {
34418012Smckusick 				close(prev[1]);
34518012Smckusick 				close(next[0]);
34618012Smckusick 			} else {		    /* Insert initial token */
34719982Smckusick 				if ((ret = write(next[1], &tok, 1)) != 1)
34819982Smckusick 					ringerr(ret, "cannot start token");
34918012Smckusick 			}
35018012Smckusick 			doslave(i, cmd[0], prev[0], next[1]);
35118012Smckusick 			close(next[1]);
35218012Smckusick 			j = read(prev[0], &tok, 1);   /* Eat the final token */
35318012Smckusick #ifdef RDUMP				    /* Read remaining acknowledges */
35418012Smckusick 			for (; j > 0 && (tok &~ OK) > 0; tok--) {
35518012Smckusick 				if (rmtwrite2() != writesize && (tok & OK)) {
35618012Smckusick 					kill(master, SIGIOT);
35718012Smckusick 					tok &= ~OK;
35818012Smckusick 				}
35918012Smckusick 			}
36018012Smckusick #endif
36118012Smckusick 			Exit(X_FINOK);
36218012Smckusick 		}
36318012Smckusick 		close(cmd[0]);
36418012Smckusick 		close(next[1]);
36518012Smckusick 		close(prev[0]);
36618012Smckusick 		prev[0] = next[0];
36718012Smckusick 	}
36818012Smckusick 	master = 0;
36918012Smckusick }
37018012Smckusick 
37118012Smckusick /*
37218012Smckusick  * Somebody must have died, should never happen
37318012Smckusick  */
37419982Smckusick ringerr(code, msg, a1, a2)
37519982Smckusick 	int code;
37619982Smckusick 	char *msg;
37719982Smckusick 	int a1, a2;
37818012Smckusick {
37919982Smckusick 	char buf[BUFSIZ];
38019982Smckusick 
38119982Smckusick 	fprintf(stderr, "  DUMP: ");
38219982Smckusick 	sprintf(buf, msg, a1, a2);
38319982Smckusick 	if (code < 0)
38419982Smckusick 		perror(msg);
38519982Smckusick 	else if (code == 0)
38619982Smckusick 		fprintf(stderr, "%s: unexpected EOF\n", buf);
38719982Smckusick 	else
38819982Smckusick 		fprintf(stderr, "%s: code %d\n", buf, code);
38918012Smckusick 	kill(master, SIGPIPE);
39018012Smckusick 	Exit(X_ABORT);
39118012Smckusick }
39218012Smckusick 
39319982Smckusick int childnum;
39419982Smckusick sigpipe()
39519982Smckusick {
39619982Smckusick 
39719982Smckusick 	ringerr(childnum, "SIGPIPE raised");
39819982Smckusick }
39919982Smckusick 
40018012Smckusick doslave(num, cmd, prev, next)
40118012Smckusick 	int num, cmd, prev, next;
40218012Smckusick {
40319982Smckusick 	int ret;
40419982Smckusick 
40518012Smckusick 	tmsg("slave %d\n", num);
40618012Smckusick 	signal(SIGINT, SIG_IGN); 		/* Master handles it */
40718012Smckusick 	signal(SIGTERM, SIG_IGN);
40819982Smckusick 	signal(SIGPIPE, sigpipe);
40919982Smckusick 	childnum = num;
41018012Smckusick 	close(fi);
41118012Smckusick 	if ((fi = open(disk, 0)) < 0) {		/* Need our own seek pointer */
41218012Smckusick 		perror("  DUMP: can't reopen disk");
41318012Smckusick 		kill(master, SIGPIPE);
41418012Smckusick 		Exit(X_ABORT);
41518012Smckusick 	}
41619982Smckusick 	while ((ret = readpipe(cmd, req, reqsiz)) == reqsiz) {
41718012Smckusick 		register struct req *p = req;
41818012Smckusick 		for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) {
41918012Smckusick 			if (p->dblk) {
42018012Smckusick 				tmsg("%d READS %d\n", num, p->count);
42118012Smckusick 				bread(p->dblk, tblock[trecno],
42218012Smckusick 				    p->count * TP_BSIZE);
42318012Smckusick 			} else {
42418012Smckusick 				tmsg("%d PIPEIN %d\n", num, p->count);
42518012Smckusick 				if (p->count != 1)
42619982Smckusick 					ringerr(11, "%d PIPEIN %d", num,
42719982Smckusick 						p->count);
42819947Smckusick 				if (readpipe(cmd, tblock[trecno], TP_BSIZE) != TP_BSIZE)
42918012Smckusick 					senderr();
43018012Smckusick 			}
43118012Smckusick 		}
43219982Smckusick 		if ((ret = read(prev, &tok, 1)) != 1)
43319982Smckusick 			ringerr(ret, "read token");	/* Wait your turn */
43418012Smckusick 		tmsg("%d WRITE\n", num);
43518012Smckusick #ifdef RDUMP
43618012Smckusick 		if (tok & OK) {
43718012Smckusick 			rmtwrite0(writesize);
43818012Smckusick 			rmtwrite1(tblock[0], writesize);
43918012Smckusick 			tok++;		/* Number of writes in progress */
44018012Smckusick 		}
44118012Smckusick 		if (tok > (LAG|OK) && (--tok, rmtwrite2() != writesize)) {
44218012Smckusick #else
44318012Smckusick 		if ((tok & OK) &&
44418012Smckusick 		    write(to, tblock[0], writesize) != writesize) {
44518012Smckusick 			perror(tape);
44618012Smckusick #endif
44718012Smckusick 			kill(master, SIGIOT);	/* restart from checkpoint */
44818012Smckusick 			tok &= ~OK;
44918012Smckusick 		}
45019982Smckusick 		if ((ret = write(next, &tok, 1)) != 1)
45119982Smckusick 			ringerr(ret, "write token"); /* Next slave's turn */
45218012Smckusick 	}
45319982Smckusick 	if (ret != 0)
45419982Smckusick 		ringerr(ret, "partial record?");
45518012Smckusick 	tmsg("%d CLOSE\n", num);
45618012Smckusick }
45719947Smckusick 
45819947Smckusick /*
45919947Smckusick  * Since a read from a pipe may not return all we asked for
46019947Smckusick  * we must loop until we get all we need
46119947Smckusick  */
46219947Smckusick readpipe(fd, buf, cnt)
46319947Smckusick 	int fd;
46419947Smckusick 	char *buf;
46519947Smckusick 	int cnt;
46619947Smckusick {
46719947Smckusick 	int rd, got;
46819947Smckusick 
46919947Smckusick 	for (rd = cnt; rd > 0; rd -= got) {
47019947Smckusick 		got = read(fd, buf, rd);
47119947Smckusick 		if (got < 0)
47219947Smckusick 			return (got);
47319947Smckusick 		if (got == 0)
47419947Smckusick 			return (cnt - rd);
47519947Smckusick 		buf += got;
47619947Smckusick 	}
47719947Smckusick 	return (cnt);
47819947Smckusick }
479