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: }