xref: /csrg-svn/sbin/dump/tape.c (revision 47056)
1 /*
2  * Copyright (c) 1980,1991 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.16 (Berkeley) 03/07/91";
9 #endif /* not lint */
10 
11 #include <sys/param.h>
12 #include <sys/wait.h>
13 #include <ufs/dir.h>
14 #include <ufs/dinode.h>
15 #include <ufs/fs.h>
16 #include <signal.h>
17 #include <fcntl.h>
18 #include <protocols/dumprestore.h>
19 #include <errno.h>
20 #ifdef __STDC__
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #endif
25 #include "dump.h"
26 #include "pathnames.h"
27 
28 char	(*tblock)[TP_BSIZE];	/* pointer to malloc()ed buffer for tape */
29 int	writesize;		/* size of malloc()ed buffer for tape */
30 long	lastspclrec = -1;	/* tape block number of last written header */
31 int	trecno = 0;		/* next record to write in current block */
32 extern	long blocksperfile;	/* number of blocks per output file */
33 extern int ntrec;		/* blocking factor on tape */
34 extern int cartridge;
35 char	*nexttape;
36 #ifdef RDUMP
37 extern char *host;
38 int	rmtopen(), rmtwrite();
39 void	rmtclose();
40 #endif RDUMP
41 
42 int	atomic();
43 void	doslave(), enslave(), flushtape(), killall();
44 
45 /*
46  * Concurrent dump mods (Caltech) - disk block reading and tape writing
47  * are exported to several slave processes.  While one slave writes the
48  * tape, the others read disk blocks; they pass control of the tape in
49  * a ring via flock().	The parent process traverses the filesystem and
50  * sends writeheader()'s and lists of daddr's to the slaves via pipes.
51  */
52 struct req {			/* instruction packets sent to slaves */
53 	daddr_t dblk;
54 	int count;
55 } *req;
56 int reqsiz;
57 
58 #define SLAVES 3		/* 1 slave writing, 1 reading, 1 for slack */
59 int slavefd[SLAVES];		/* pipes from master to each slave */
60 int slavepid[SLAVES];		/* used by killall() */
61 int rotor;			/* next slave to be instructed */
62 int master;			/* pid of master, for sending error signals */
63 int tenths;			/* length of tape used per block written */
64 
65 int
66 alloctape()
67 {
68 	int pgoff = getpagesize() - 1;
69 
70 	writesize = ntrec * TP_BSIZE;
71 	reqsiz = ntrec * sizeof(struct req);
72 	/*
73 	 * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
74 	 * (see DEC TU80 User's Guide).  The shorter gaps of 6250-bpi require
75 	 * repositioning after stopping, i.e, streaming mode, where the gap is
76 	 * variable, 0.30" to 0.45".  The gap is maximal when the tape stops.
77 	 */
78 	if (blocksperfile == 0)
79 		tenths = writesize / density +
80 		    (cartridge ? 16 : density == 625 ? 5 : 8);
81 	/*
82 	 * Allocate tape buffer contiguous with the array of instruction
83 	 * packets, so flushtape() can write them together with one write().
84 	 * Align tape buffer on page boundary to speed up tape write().
85 	 */
86 	req = (struct req *)malloc(reqsiz + writesize + pgoff);
87 	if (req == NULL)
88 		return(0);
89 	tblock = (char (*)[TP_BSIZE]) (((long)&req[ntrec] + pgoff) &~ pgoff);
90 	req = (struct req *)tblock - ntrec;
91 	return(1);
92 }
93 
94 
95 void
96 writerec(dp)
97 	char *dp;
98 {
99 	req[trecno].dblk = (daddr_t)0;
100 	req[trecno].count = 1;
101 	*(union u_spcl *)(*tblock++) = *(union u_spcl *)dp;	/* movc3 */
102 	lastspclrec = spcl.c_tapea;
103 	trecno++;
104 	spcl.c_tapea++;
105 	if (trecno >= ntrec)
106 		flushtape();
107 }
108 
109 void
110 dumpblock(blkno, size)
111 	daddr_t blkno;
112 	int size;
113 {
114 	int avail, tpblks, dblkno;
115 
116 	dblkno = fsbtodb(sblock, blkno);
117 	tpblks = size >> tp_bshift;
118 	while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
119 		req[trecno].dblk = dblkno;
120 		req[trecno].count = avail;
121 		trecno += avail;
122 		spcl.c_tapea += avail;
123 		if (trecno >= ntrec)
124 			flushtape();
125 		dblkno += avail << (tp_bshift - dev_bshift);
126 		tpblks -= avail;
127 	}
128 }
129 
130 int	nogripe = 0;
131 
132 void
133 tperror()
134 {
135 	if (pipeout) {
136 		msg("write error on %s\n", tape);
137 		quit("Cannot recover\n");
138 		/* NOTREACHED */
139 	}
140 	msg("write error %d blocks into volume %d\n", blockswritten, tapeno);
141 	broadcast("DUMP WRITE ERROR!\n");
142 	if (!query("Do you want to restart?"))
143 		dumpabort();
144 	msg("Closing this volume.  Prepare to restart with new media;\n");
145 	msg("this dump volume will be rewritten.\n");
146 	killall();
147 	nogripe = 1;
148 	close_rewind();
149 	Exit(X_REWRITE);
150 }
151 
152 void
153 sigpipe()
154 {
155 
156 	quit("Broken pipe\n");
157 }
158 
159 void
160 flushtape()
161 {
162 #ifndef __STDC__
163 	int write();
164 #endif
165 
166 	int siz = (char *)tblock - (char *)req;
167 
168 	if (atomic(write, slavefd[rotor], req, siz) != siz)
169 		quit("error writing command pipe: %s\n", strerror(errno));
170 	if (++rotor >= SLAVES)
171 		rotor = 0;
172 	tblock = (char (*)[TP_BSIZE]) &req[ntrec];
173 	trecno = 0;
174 	asize += tenths;
175 	blockswritten += ntrec;
176 	if (!pipeout && (blocksperfile ?
177 	    (blockswritten >= blocksperfile) : (asize > tsize))) {
178 		close_rewind();
179 		startnewtape();
180 	}
181 	timeest();
182 }
183 
184 void
185 trewind()
186 {
187 	int f;
188 
189 	if (pipeout)
190 		return;
191 	for (f = 0; f < SLAVES; f++)
192 		close(slavefd[f]);
193 	while (wait((int *)NULL) >= 0)	/* wait for any signals from slaves */
194 		/* void */;
195 	msg("Tape rewinding\n");
196 #ifdef RDUMP
197 	if (host) {
198 		rmtclose();
199 		while (rmtopen(tape, 0) < 0)
200 			sleep(10);
201 		rmtclose();
202 		return;
203 	}
204 #endif RDUMP
205 	close(tapefd);
206 	while ((f = open(tape, 0)) < 0)
207 		sleep (10);
208 	close(f);
209 }
210 
211 void
212 close_rewind()
213 {
214 	trewind();
215 	if (!nogripe) {
216 		msg("Change Volumes: Mount volume #%d\n", tapeno+1);
217 		broadcast("CHANGE DUMP VOLUMES!\7\7\n");
218 	}
219 	while (nexttape == 0 &&
220 	    !query("Is the new volume mounted and ready to go?"))
221 		if (query("Do you want to abort?")) {
222 			dumpabort();
223 			/*NOTREACHED*/
224 		}
225 }
226 
227 /*
228  *	We implement taking and restoring checkpoints on the tape level.
229  *	When each tape is opened, a new process is created by forking; this
230  *	saves all of the necessary context in the parent.  The child
231  *	continues the dump; the parent waits around, saving the context.
232  *	If the child returns X_REWRITE, then it had problems writing that tape;
233  *	this causes the parent to fork again, duplicating the context, and
234  *	everything continues as if nothing had happened.
235  */
236 
237 void
238 startnewtape()
239 {
240 	int	parentpid;
241 	int	childpid;
242 	int	status;
243 	int	waitpid;
244 	sig_t	interrupt;
245 	int	blks, i;
246 	char	*p;
247 
248 	interrupt = signal(SIGINT, SIG_IGN);
249 	parentpid = getpid();
250 
251     restore_check_point:
252 	(void)signal(SIGINT, interrupt);
253 	/*
254 	 *	All signals are inherited...
255 	 */
256 	childpid = fork();
257 	if (childpid < 0) {
258 		msg("Context save fork fails in parent %d\n", parentpid);
259 		Exit(X_ABORT);
260 	}
261 	if (childpid != 0) {
262 		/*
263 		 *	PARENT:
264 		 *	save the context by waiting
265 		 *	until the child doing all of the work returns.
266 		 *	don't catch the interrupt
267 		 */
268 		signal(SIGINT, SIG_IGN);
269 #ifdef TDEBUG
270 		msg("Tape: %d; parent process: %d child process %d\n",
271 			tapeno+1, parentpid, childpid);
272 #endif TDEBUG
273 		while ((waitpid = wait(&status)) != childpid)
274 			msg("Parent %d waiting for child %d has another child %d return\n",
275 				parentpid, childpid, waitpid);
276 		if (status & 0xFF) {
277 			msg("Child %d returns LOB status %o\n",
278 				childpid, status&0xFF);
279 		}
280 		status = (status >> 8) & 0xFF;
281 #ifdef TDEBUG
282 		switch(status) {
283 			case X_FINOK:
284 				msg("Child %d finishes X_FINOK\n", childpid);
285 				break;
286 			case X_ABORT:
287 				msg("Child %d finishes X_ABORT\n", childpid);
288 				break;
289 			case X_REWRITE:
290 				msg("Child %d finishes X_REWRITE\n", childpid);
291 				break;
292 			default:
293 				msg("Child %d finishes unknown %d\n",
294 					childpid, status);
295 				break;
296 		}
297 #endif TDEBUG
298 		switch(status) {
299 			case X_FINOK:
300 				Exit(X_FINOK);
301 			case X_ABORT:
302 				Exit(X_ABORT);
303 			case X_REWRITE:
304 				goto restore_check_point;
305 			default:
306 				msg("Bad return code from dump: %d\n", status);
307 				Exit(X_ABORT);
308 		}
309 		/*NOTREACHED*/
310 	} else {	/* we are the child; just continue */
311 #ifdef TDEBUG
312 		sleep(4);	/* allow time for parent's message to get out */
313 		msg("Child on Tape %d has parent %d, my pid = %d\n",
314 			tapeno+1, parentpid, getpid());
315 #endif TDEBUG
316 		/*
317 		 * If we have a name like "/dev/rmt0,/dev/rmt1",
318 		 * use the name before the comma first, and save
319 		 * the second name for next time.
320 		 */
321 		if (nexttape && *nexttape)
322 			tape = nexttape;
323 		if (p = index(tape, ',')) {
324 			*p = '\0';
325 			nexttape = p + 1;
326 		} else
327 			nexttape = NULL;
328 #ifdef RDUMP
329 		while ((tapefd = (host ? rmtopen(tape, 2) :
330 			pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
331 #else RDUMP
332 		while ((tapefd =
333 			pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666)) < 0)
334 #endif RDUMP
335 		    {
336 			msg("Cannot open output \"%s\".\n", tape);
337 			if (!query("Do you want to retry the open?"))
338 				dumpabort();
339 		}
340 
341 		enslave();  /* Share open tape file descriptor with slaves */
342 
343 		asize = 0;
344 		tapeno++;		/* current tape sequence */
345 		newtape++;		/* new tape signal */
346 		blks = 0;
347 		if (spcl.c_type != TS_END)
348 			for (i = 0; i < spcl.c_count; i++)
349 				if (spcl.c_addr[i] != 0)
350 					blks++;
351 		spcl.c_count = blks + 1 - spcl.c_tapea + lastspclrec;
352 		spcl.c_volume++;
353 		spcl.c_type = TS_TAPE;
354 		spcl.c_flags |= DR_NEWHEADER;
355 		writeheader(curino);
356 		spcl.c_flags &=~ DR_NEWHEADER;
357 		if (tapeno > 1)
358 			msg("Tape %d begins with blocks from inode %d\n",
359 				tapeno, curino);
360 	}
361 }
362 
363 void
364 dumpabort()
365 {
366 	if (master != 0 && master != getpid())
367 		kill(master, SIGTERM);	/* Signals master to call dumpabort */
368 	else {
369 		killall();
370 		msg("The ENTIRE dump is aborted.\n");
371 	}
372 	Exit(X_ABORT);
373 }
374 
375 void
376 Exit(status)
377 	int status;
378 {
379 #ifdef TDEBUG
380 	msg("pid = %d exits with status %d\n", getpid(), status);
381 #endif TDEBUG
382 	exit(status);
383 }
384 
385 /*
386  * could use pipe() for this if flock() worked on pipes
387  */
388 void
389 lockfile(fd)
390 	int fd[2];
391 {
392 	char tmpname[20];
393 
394 	strcpy(tmpname, _PATH_LOCK);
395 	mktemp(tmpname);
396 	if ((fd[1] = creat(tmpname, 0400)) < 0)
397 		quit("cannot create lockfile %s: %s\n",
398 		    tmpname, strerror(errno));
399 	if ((fd[0] = open(tmpname, 0)) < 0)
400 		quit("cannot reopen lockfile %s: %s\n",
401 		    tmpname, strerror(errno));
402 	(void) unlink(tmpname);
403 }
404 
405 void
406 enslave()
407 {
408 	int first[2], prev[2], next[2], cmd[2];     /* file descriptors */
409 	register int i, j;
410 
411 	master = getpid();
412 	signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */
413 	signal(SIGPIPE, sigpipe);
414 	signal(SIGUSR1, tperror);    /* Slave sends SIGUSR1 on tape errors */
415 	lockfile(first);
416 	for (i = 0; i < SLAVES; i++) {
417 		if (i == 0) {
418 			prev[0] = first[1];
419 			prev[1] = first[0];
420 		} else {
421 			prev[0] = next[0];
422 			prev[1] = next[1];
423 			flock(prev[1], LOCK_EX);
424 		}
425 		if (i < SLAVES - 1) {
426 			lockfile(next);
427 		} else {
428 			next[0] = first[0];
429 			next[1] = first[1];	    /* Last slave loops back */
430 		}
431 		if (pipe(cmd) < 0 || (slavepid[i] = fork()) < 0)
432 			quit("too many slaves, %d (recompile smaller): %s\n",
433 			    i, strerror(errno));
434 		slavefd[i] = cmd[1];
435 		if (slavepid[i] == 0) { 	    /* Slave starts up here */
436 			for (j = 0; j <= i; j++)
437 				close(slavefd[j]);
438 			signal(SIGINT, SIG_IGN);    /* Master handles this */
439 			doslave(cmd[0], prev, next);
440 			Exit(X_FINOK);
441 		}
442 		close(cmd[0]);
443 		if (i > 0) {
444 			close(prev[0]);
445 			close(prev[1]);
446 		}
447 	}
448 	close(first[0]);
449 	close(first[1]);
450 	master = 0; rotor = 0;
451 }
452 
453 void
454 killall()
455 {
456 	register int i;
457 
458 	for (i = 0; i < SLAVES; i++)
459 		if (slavepid[i] > 0)
460 			kill(slavepid[i], SIGKILL);
461 }
462 
463 /*
464  * Synchronization - each process has a lockfile, and shares file
465  * descriptors to the following process's lockfile.  When our write
466  * completes, we release our lock on the following process's lock-
467  * file, allowing the following process to lock it and proceed. We
468  * get the lock back for the next cycle by swapping descriptors.
469  */
470 void
471 doslave(cmd, prev, next)
472 	register int cmd, prev[2], next[2];
473 {
474 	register int nread, toggle = 0;
475 #ifndef __STDC__
476 	int read();
477 #endif
478 
479 	/*
480 	 * Need our own seek pointer.
481 	 */
482 	close(diskfd);
483 	if ((diskfd = open(disk, O_RDONLY)) < 0)
484 		quit("slave couldn't reopen disk: %s\n", strerror(errno));
485 	/*
486 	 * Get list of blocks to dump, read the blocks into tape buffer
487 	 */
488 	while ((nread = atomic(read, cmd, req, reqsiz)) == reqsiz) {
489 		register struct req *p = req;
490 		for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) {
491 			if (p->dblk) {
492 				bread(p->dblk, tblock[trecno],
493 					p->count * TP_BSIZE);
494 			} else {
495 				if (p->count != 1 || atomic(read, cmd,
496 				    tblock[trecno], TP_BSIZE) != TP_BSIZE)
497 					quit("master/slave protocol botched.\n");
498 			}
499 		}
500 		flock(prev[toggle], LOCK_EX);	/* Wait our turn */
501 
502 #ifdef RDUMP
503 		if ((host ? rmtwrite(tblock[0], writesize)
504 			: write(tapefd, tblock[0], writesize)) != writesize) {
505 #else RDUMP
506 		if (write(tapefd, tblock[0], writesize) != writesize) {
507 #endif RDUMP
508 			kill(master, SIGUSR1);
509 			for (;;)
510 				sigpause(0);
511 		}
512 		toggle ^= 1;
513 		flock(next[toggle], LOCK_UN);	/* Next slave's turn */
514 	}					/* Also jolts him awake */
515 	if (nread != 0)
516 		quit("error reading command pipe: %s\n", strerror(errno));
517 }
518 
519 /*
520  * Since a read from a pipe may not return all we asked for,
521  * or a write may not write all we ask if we get a signal,
522  * loop until the count is satisfied (or error).
523  */
524 int
525 atomic(func, fd, buf, count)
526 	int (*func)(), fd, count;
527 	char *buf;
528 {
529 	int got, need = count;
530 
531 	while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
532 		buf += got;
533 	return (got < 0 ? got : count - need);
534 }
535