|
|
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.8 (Berkeley) 2/23/87";
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: long lastspclrec = -1; /* tape block number of last written header */
17: int trecno = 0; /* next record to write in current block */
18: extern int ntrec; /* blocking factor on tape */
19: extern int cartridge;
20: extern int read(), write();
21: #ifdef RDUMP
22: extern char *host;
23: #endif RDUMP
24:
25: /*
26: * Concurrent dump mods (Caltech) - disk block reading and tape writing
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
29: * a ring via flock(). The parent process traverses the filesystem and
30: * sends spclrec()'s and lists of daddr's to the slaves via pipes.
31: */
32: struct req { /* instruction packets sent to slaves */
33: daddr_t dblk;
34: int count;
35: } *req;
36: int reqsiz;
37:
38: #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */
39: int slavefd[SLAVES]; /* pipes from master to each slave */
40: int slavepid[SLAVES]; /* used by killall() */
41: int rotor; /* next slave to be instructed */
42: int master; /* pid of master, for sending error signals */
43: int tenths; /* length of tape used per block written */
44:
45: alloctape()
46: {
47: int pgoff = getpagesize() - 1;
48:
49: writesize = ntrec * TP_BSIZE;
50: reqsiz = ntrec * sizeof(struct req);
51: /*
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().
62: */
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);
67: req = (struct req *)tblock - ntrec;
68: return(1);
69: }
70:
71:
72: taprec(dp)
73: char *dp;
74: {
75: req[trecno].dblk = (daddr_t)0;
76: req[trecno].count = 1;
77: *(union u_spcl *)(*tblock++) = *(union u_spcl *)dp; /* movc3 */
78: lastspclrec = spcl.c_tapea;
79: trecno++;
80: spcl.c_tapea++;
81: if(trecno >= ntrec)
82: flusht();
83: }
84:
85: dmpblk(blkno, size)
86: daddr_t blkno;
87: int size;
88: {
89: int avail, tpblks, dblkno;
90:
91: dblkno = fsbtodb(sblock, blkno);
92: tpblks = size / TP_BSIZE;
93: while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
94: req[trecno].dblk = dblkno;
95: req[trecno].count = avail;
96: trecno += avail;
97: spcl.c_tapea += avail;
98: if (trecno >= ntrec)
99: flusht();
100: dblkno += avail * (TP_BSIZE / dev_bsize);
101: tpblks -= avail;
102: }
103: }
104:
105: int nogripe = 0;
106:
107: tperror() {
108: if (pipeout) {
109: msg("Tape write error on %s\n", tape);
110: msg("Cannot recover\n");
111: dumpabort();
112: /* NOTREACHED */
113: }
114: msg("Tape write error %d feet into tape %d\n", asize/120L, tapeno);
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");
121: killall();
122: nogripe = 1;
123: close_rewind();
124: Exit(X_REWRITE);
125: }
126:
127: sigpipe()
128: {
129:
130: msg("Broken pipe\n");
131: dumpabort();
132: }
133:
134: #ifdef RDUMP
135: /*
136: * compatibility routine
137: */
138: tflush(i)
139: int i;
140: {
141:
142: for (i = 0; i < ntrec; i++)
143: spclrec();
144: }
145: #endif RDUMP
146:
147: flusht()
148: {
149: int siz = (char *)tblock - (char *)req;
150:
151: if (atomic(write, slavefd[rotor], req, siz) != siz) {
152: perror(" DUMP: error writing command pipe");
153: dumpabort();
154: }
155: if (++rotor >= SLAVES) rotor = 0;
156: tblock = (char (*)[TP_BSIZE]) &req[ntrec];
157: trecno = 0;
158: asize += tenths;
159: blockswritten += ntrec;
160: if (!pipeout && asize > tsize) {
161: close_rewind();
162: otape();
163: }
164: timeest();
165: }
166:
167: rewind()
168: {
169: int f;
170:
171: if (pipeout)
172: return;
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
178: if (host) {
179: rmtclose();
180: while (rmtopen(tape, 0) < 0)
181: sleep(10);
182: rmtclose();
183: return;
184: }
185: #endif RDUMP
186: close(to);
187: while ((f = open(tape, 0)) < 0)
188: sleep (10);
189: close(f);
190: }
191:
192: close_rewind()
193: {
194: rewind();
195: if (!nogripe) {
196: msg("Change Tapes: Mount tape #%d\n", tapeno+1);
197: broadcast("CHANGE TAPES!\7\7\n");
198: }
199: while (!query("Is the new tape mounted and ready to go?"))
200: if (query("Do you want to abort?")) {
201: dumpabort();
202: /*NOTREACHED*/
203: }
204: }
205:
206: /*
207: * We implement taking and restoring checkpoints on the tape level.
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:
216: otape()
217: {
218: int parentpid;
219: int childpid;
220: int status;
221: int waitpid;
222: int (*interrupt)() = signal(SIGINT, SIG_IGN);
223: int blks, i;
224:
225: parentpid = getpid();
226:
227: restore_check_point:
228: signal(SIGINT, interrupt);
229: /*
230: * All signals are inherited...
231: */
232: childpid = fork();
233: if (childpid < 0) {
234: msg("Context save fork fails in parent %d\n", parentpid);
235: Exit(X_ABORT);
236: }
237: if (childpid != 0) {
238: /*
239: * PARENT:
240: * save the context by waiting
241: * until the child doing all of the work returns.
242: * don't catch the interrupt
243: */
244: signal(SIGINT, SIG_IGN);
245: #ifdef TDEBUG
246: msg("Tape: %d; parent process: %d child process %d\n",
247: tapeno+1, parentpid, childpid);
248: #endif TDEBUG
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) {
253: msg("Child %d returns LOB status %o\n",
254: childpid, status&0xFF);
255: }
256: status = (status >> 8) & 0xFF;
257: #ifdef TDEBUG
258: switch(status) {
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:
269: msg("Child %d finishes unknown %d\n",
270: childpid, status);
271: break;
272: }
273: #endif TDEBUG
274: switch(status) {
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());
291: #endif TDEBUG
292: #ifdef RDUMP
293: while ((to = (host ? rmtopen(tape, 2) :
294: pipeout ? 1 : creat(tape, 0666))) < 0)
295: #else RDUMP
296: while ((to = pipeout ? 1 : creat(tape, 0666)) < 0)
297: #endif RDUMP
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 */
302:
303: asize = 0;
304: tapeno++; /* current tape sequence */
305: newtape++; /* new tape signal */
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;
312: spcl.c_volume++;
313: spcl.c_type = TS_TAPE;
314: spcl.c_flags |= DR_NEWHEADER;
315: spclrec();
316: spcl.c_flags &=~ DR_NEWHEADER;
317: if (tapeno > 1)
318: msg("Tape %d begins with blocks from ino %d\n",
319: tapeno, ino);
320: }
321: }
322:
323: dumpabort()
324: {
325: if (master != 0 && master != getpid())
326: kill(master, SIGTERM); /* Signals master to call dumpabort */
327: else {
328: killall();
329: msg("The ENTIRE dump is aborted.\n");
330: }
331: Exit(X_ABORT);
332: }
333:
334: Exit(status)
335: {
336: #ifdef TDEBUG
337: msg("pid = %d exits with status %d\n", getpid(), status);
338: #endif TDEBUG
339: exit(status);
340: }
341:
342: /*
343: * could use pipe() for this if flock() worked on pipes
344: */
345: lockfile(fd)
346: int fd[2];
347: {
348: char tmpname[20];
349:
350: strcpy(tmpname, "/tmp/dumplockXXXXXX");
351: mktemp(tmpname);
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: }
362: unlink(tmpname);
363: }
364:
365: enslave()
366: {
367: int first[2], prev[2], next[2], cmd[2]; /* file descriptors */
368: register int i, j;
369:
370: master = getpid();
371: signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */
372: signal(SIGPIPE, sigpipe);
373: signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */
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: }
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("");
393: dumpabort();
394: }
395: slavefd[i] = cmd[1];
396: if (slavepid[i] == 0) { /* Slave starts up here */
397: for (j = 0; j <= i; j++)
398: close(slavefd[j]);
399: signal(SIGINT, SIG_IGN); /* Master handles this */
400: doslave(cmd[0], prev, next);
401: Exit(X_FINOK);
402: }
403: close(cmd[0]);
404: if (i > 0) {
405: close(prev[0]);
406: close(prev[1]);
407: }
408: }
409: close(first[0]);
410: close(first[1]);
411: master = 0; rotor = 0;
412: }
413:
414: killall()
415: {
416: register int i;
417:
418: for (i = 0; i < SLAVES; i++)
419: if (slavepid[i] > 0)
420: kill(slavepid[i], SIGKILL);
421: }
422:
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: */
430: doslave(cmd, prev, next)
431: register int cmd, prev[2], next[2];
432: {
433: register int nread, toggle = 0;
434:
435: close(fi);
436: if ((fi = open(disk, 0)) < 0) { /* Need our own seek pointer */
437: perror(" DUMP: slave couldn't reopen disk");
438: dumpabort();
439: }
440: /*
441: * Get list of blocks to dump, read the blocks into tape buffer
442: */
443: while ((nread = atomic(read, cmd, req, reqsiz)) == reqsiz) {
444: register struct req *p = req;
445: for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) {
446: if (p->dblk) {
447: bread(p->dblk, tblock[trecno],
448: p->count * TP_BSIZE);
449: } else {
450: if (p->count != 1 || atomic(read, cmd,
451: tblock[trecno], TP_BSIZE) != TP_BSIZE) {
452: msg("Master/slave protocol botched.\n");
453: dumpabort();
454: }
455: }
456: }
457: flock(prev[toggle], LOCK_EX); /* Wait our turn */
458:
459: #ifdef RDUMP
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) {
464: #endif RDUMP
465: kill(master, SIGUSR1);
466: for (;;)
467: sigpause(0);
468: }
469: toggle ^= 1;
470: flock(next[toggle], LOCK_UN); /* Next slave's turn */
471: } /* Also jolts him awake */
472: if (nread != 0) {
473: perror(" DUMP: error reading command pipe");
474: dumpabort();
475: }
476: }
477:
478: /*
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).
482: */
483: atomic(func, fd, buf, count)
484: int (*func)(), fd, count;
485: char *buf;
486: {
487: int got, need = count;
488:
489: while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
490: buf += got;
491: return (got < 0 ? got : count - need);
492: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.