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