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