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