|
|
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.