1*22040Sdist /* 2*22040Sdist * Copyright (c) 1980 Regents of the University of California. 3*22040Sdist * All rights reserved. The Berkeley software License Agreement 4*22040Sdist * specifies the terms and conditions for redistribution. 5*22040Sdist */ 6*22040Sdist 717527Ssam #ifndef lint 8*22040Sdist static char sccsid[] = "@(#)tape.c 5.1 (Berkeley) 06/05/85"; 9*22040Sdist #endif not lint 1017527Ssam 111425Sroot #include "dump.h" 1218012Smckusick #include <signal.h> 131425Sroot 1410911Ssam char (*tblock)[TP_BSIZE]; /* Pointer to malloc()ed buffer for tape */ 1510911Ssam int writesize; /* Size of malloc()ed buffer for tape */ 164774Smckusic int trecno = 0; 1718012Smckusick extern int ntrec; /* blocking factor on tape */ 181425Sroot 1910911Ssam /* 2018012Smckusick * Streaming dump mods (Caltech) - disk block reading and tape writing 2118012Smckusick * are exported to several slave processes. While one slave writes the 2218012Smckusick * tape, the others read disk blocks; they pass control of the tape in 2318012Smckusick * a ring via pipes. The parent process traverses the filesystem and 2418012Smckusick * sends daddr's, inode records, etc, through pipes to each slave. 2518012Smckusick * Speed from Eagle to TU77 on VAX/780 is about 140 Kbytes/second. 2618012Smckusick * #ifdef RDUMP version is CPU-limited to about 40 Kbytes/second. 2710911Ssam */ 2818012Smckusick struct req { /* instruction packets sent to slaves */ 2918012Smckusick daddr_t dblk; 3018012Smckusick int count; 3118012Smckusick } *req; 3218012Smckusick int reqsiz; 3318012Smckusick 3418012Smckusick #define SLAVES 3 /* 2 slaves read disk while 3rd writes tape */ 3518012Smckusick #define LAG 2 /* Write behind by LAG tape blocks (rdump) */ 3618012Smckusick int slavefd[SLAVES]; /* Pipes from master to each slave */ 3718012Smckusick int rotor; /* Current slave number */ 3818012Smckusick int master; /* Pid of master, for sending error signals */ 3918012Smckusick int trace = 0; /* Protocol trace; easily patchable with adb */ 4018012Smckusick #define tmsg if (trace) msg 4118012Smckusick 4218012Smckusick #ifdef RDUMP 4318012Smckusick extern int rmtape; 4418012Smckusick #endif 4518012Smckusick 4618012Smckusick /* 4718012Smckusick * Allocate tape buffer contiguous with the array of instruction packets, 4818012Smckusick * so they can be written with a single write call in flusht(). 4918012Smckusick */ 5010911Ssam alloctape() 5110911Ssam { 5210911Ssam 5310911Ssam writesize = ntrec * TP_BSIZE; 5418012Smckusick reqsiz = ntrec * sizeof(struct req); 5518012Smckusick req = (struct req *)malloc(reqsiz+writesize); /* array of packets */ 5618012Smckusick tblock = (char (*)[TP_BSIZE]) &req[ntrec]; /* Tape buffer */ 5718012Smckusick return (req != NULL); 5810911Ssam } 5910911Ssam 6018012Smckusick /* 6118012Smckusick * Send special record to be put on tape 6218012Smckusick */ 631425Sroot taprec(dp) 645329Smckusic char *dp; 651425Sroot { 661425Sroot 6718012Smckusick tmsg("taprec %d\n", trecno); 6818012Smckusick req[trecno].dblk = (daddr_t)0; 6918012Smckusick req[trecno].count = 1; 7018012Smckusick *(union u_spcl *)(*tblock++) = *(union u_spcl *)dp; 711425Sroot spcl.c_tapea++; 7218012Smckusick if (++trecno >= ntrec) 731425Sroot flusht(); 741425Sroot } 751425Sroot 764774Smckusic dmpblk(blkno, size) 774774Smckusic daddr_t blkno; 784774Smckusic int size; 791425Sroot { 8018012Smckusick int tpblks, dblkno; 8118012Smckusick register int avail; 821425Sroot 835329Smckusic if (size % TP_BSIZE != 0) 844774Smckusic msg("bad size to dmpblk: %d\n", size); 855329Smckusic dblkno = fsbtodb(sblock, blkno); 8618012Smckusick tpblks = size / TP_BSIZE; 8718012Smckusick while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { 8818012Smckusick tmsg("dmpblk %d\n", avail); 8918012Smckusick req[trecno].dblk = dblkno; 9018012Smckusick req[trecno].count = avail; 914774Smckusic trecno += avail; 924774Smckusic spcl.c_tapea += avail; 9318012Smckusick if (trecno >= ntrec) 9418012Smckusick flusht(); 955329Smckusic dblkno += avail * (TP_BSIZE / DEV_BSIZE); 965329Smckusic tpblks -= avail; 974774Smckusic } 981425Sroot } 991425Sroot 1001425Sroot int nogripe = 0; 1011425Sroot 10218012Smckusick tperror() { 10318012Smckusick if (pipeout) { 10418012Smckusick msg("Tape write error on %s\n", tape); 10518012Smckusick msg("Cannot recover\n"); 10618012Smckusick dumpabort(); 10718012Smckusick /* NOTREACHED */ 10818012Smckusick } 10918012Smckusick msg("Tape write error on tape %d\n", tapeno); 11018012Smckusick broadcast("TAPE ERROR!\n"); 11118012Smckusick if (!query("Do you want to restart?")) 11218012Smckusick dumpabort(); 11318012Smckusick msg("This tape will rewind. After it is rewound,\n"); 11418012Smckusick msg("replace the faulty tape with a new one;\n"); 11518012Smckusick msg("this dump volume will be rewritten.\n"); 11618012Smckusick nogripe = 1; 11718012Smckusick close_rewind(); 11818012Smckusick Exit(X_REWRITE); 11918012Smckusick } 12018012Smckusick 12118012Smckusick senderr() 12218012Smckusick { 12318012Smckusick 12419982Smckusick perror(" DUMP: pipe error in command to slave"); 12518012Smckusick dumpabort(); 12618012Smckusick } 12718012Smckusick 12818012Smckusick #ifdef RDUMP 12918012Smckusick tflush(cnt) 13018012Smckusick int cnt; 13118012Smckusick { 13218012Smckusick int i; 13318012Smckusick 13418012Smckusick for (i = 0; i < ntrec; i++) 13518012Smckusick spclrec(); 13618012Smckusick } 13718012Smckusick #endif RDUMP 13818012Smckusick 1391425Sroot flusht() 1401425Sroot { 14118012Smckusick int sig, siz = (char *)tblock - (char *)req; 1421425Sroot 14318012Smckusick tmsg("flusht %d\n", siz); 14418012Smckusick sig = sigblock(1<<SIGINT-1 | 1<<SIGIOT-1); /* Don't interrupt write */ 14518012Smckusick if (write(slavefd[rotor], req, siz) != siz) 14618012Smckusick senderr(); 14718012Smckusick sigsetmask(sig); 14818012Smckusick if (++rotor >= SLAVES) rotor = 0; 14918012Smckusick tblock = (char (*)[TP_BSIZE]) &req[ntrec]; 1501425Sroot trecno = 0; 15110911Ssam asize += writesize/density; 15218012Smckusick asize += 7; /* inter-record gap (why fixed?) */ 15310911Ssam blockswritten += ntrec; 15412331Smckusick if (!pipeout && asize > tsize) { 1551425Sroot close_rewind(); 1561425Sroot otape(); 1571425Sroot } 1581425Sroot timeest(); 1591425Sroot } 1601425Sroot 1611425Sroot rewind() 1621425Sroot { 16318012Smckusick register int f; 16412331Smckusick 16512331Smckusick if (pipeout) 16612331Smckusick return; 16718012Smckusick for (f = 0; f < SLAVES; f++) 16818012Smckusick close(slavefd[f]); 16918012Smckusick while (wait(NULL) >= 0) ; /* wait for any signals from slaves */ 17018012Smckusick msg("Tape rewinding\n"); 17118012Smckusick #ifdef RDUMP 17218012Smckusick rmtclose(); 17318012Smckusick while (rmtopen(tape, 0) < 0) 17418012Smckusick sleep(10); 17518012Smckusick rmtclose(); 1761425Sroot #else 1773214Swnj close(to); 1783214Swnj while ((f = open(tape, 0)) < 0) 1793214Swnj sleep (10); 1803214Swnj close(f); 1811425Sroot #endif 1821425Sroot } 1831425Sroot 1841425Sroot close_rewind() 1851425Sroot { 18618012Smckusick rewind(); 18718012Smckusick if (!nogripe) { 1881425Sroot msg("Change Tapes: Mount tape #%d\n", tapeno+1); 1891425Sroot broadcast("CHANGE TAPES!\7\7\n"); 1901425Sroot } 19118012Smckusick while (!query("Is the new tape mounted and ready to go?")) 19218012Smckusick if (query("Do you want to abort?")) 1931425Sroot dumpabort(); 1941425Sroot } 1951425Sroot 1961425Sroot /* 19718012Smckusick * We implement taking and restoring checkpoints on the tape level. 1981425Sroot * When each tape is opened, a new process is created by forking; this 1991425Sroot * saves all of the necessary context in the parent. The child 2001425Sroot * continues the dump; the parent waits around, saving the context. 2011425Sroot * If the child returns X_REWRITE, then it had problems writing that tape; 2021425Sroot * this causes the parent to fork again, duplicating the context, and 2031425Sroot * everything continues as if nothing had happened. 2041425Sroot */ 2051425Sroot 2061425Sroot otape() 2071425Sroot { 2081425Sroot int parentpid; 2091425Sroot int childpid; 2101425Sroot int status; 2111425Sroot int waitpid; 2121425Sroot int interrupt(); 2131425Sroot 2141425Sroot parentpid = getpid(); 2151425Sroot 2161425Sroot restore_check_point: 2171425Sroot signal(SIGINT, interrupt); 2181425Sroot /* 2191425Sroot * All signals are inherited... 2201425Sroot */ 2211425Sroot childpid = fork(); 22218012Smckusick if (childpid < 0) { 2231425Sroot msg("Context save fork fails in parent %d\n", parentpid); 2241425Sroot Exit(X_ABORT); 2251425Sroot } 22618012Smckusick if (childpid != 0) { 2271425Sroot /* 2281425Sroot * PARENT: 2291425Sroot * save the context by waiting 2301425Sroot * until the child doing all of the work returns. 23118012Smckusick * don't catch the interrupt 2321425Sroot */ 2331425Sroot signal(SIGINT, SIG_IGN); 2341425Sroot #ifdef TDEBUG 2351425Sroot msg("Tape: %d; parent process: %d child process %d\n", 2361425Sroot tapeno+1, parentpid, childpid); 2371425Sroot #endif TDEBUG 23818012Smckusick while ((waitpid = wait(&status)) != childpid) 23918012Smckusick msg("Parent %d waiting for child %d has another child %d return\n", 24018012Smckusick parentpid, childpid, waitpid); 24118012Smckusick if (status & 0xFF) { 2421425Sroot msg("Child %d returns LOB status %o\n", 2431425Sroot childpid, status&0xFF); 2441425Sroot } 2451425Sroot status = (status >> 8) & 0xFF; 2461425Sroot #ifdef TDEBUG 24718012Smckusick switch(status) { 2481425Sroot case X_FINOK: 2491425Sroot msg("Child %d finishes X_FINOK\n", childpid); 2501425Sroot break; 2511425Sroot case X_ABORT: 2521425Sroot msg("Child %d finishes X_ABORT\n", childpid); 2531425Sroot break; 2541425Sroot case X_REWRITE: 2551425Sroot msg("Child %d finishes X_REWRITE\n", childpid); 2561425Sroot break; 2571425Sroot default: 25818012Smckusick msg("Child %d finishes unknown %d\n", 25918012Smckusick childpid, status); 2601425Sroot break; 2611425Sroot } 2621425Sroot #endif TDEBUG 26318012Smckusick switch(status) { 2641425Sroot case X_FINOK: 2651425Sroot Exit(X_FINOK); 2661425Sroot case X_ABORT: 2671425Sroot Exit(X_ABORT); 2681425Sroot case X_REWRITE: 2691425Sroot goto restore_check_point; 2701425Sroot default: 2711425Sroot msg("Bad return code from dump: %d\n", status); 2721425Sroot Exit(X_ABORT); 2731425Sroot } 2741425Sroot /*NOTREACHED*/ 2751425Sroot } else { /* we are the child; just continue */ 2761425Sroot #ifdef TDEBUG 2771425Sroot sleep(4); /* allow time for parent's message to get out */ 2781425Sroot msg("Child on Tape %d has parent %d, my pid = %d\n", 2791425Sroot tapeno+1, parentpid, getpid()); 2801425Sroot #endif 28118012Smckusick #ifdef RDUMP 28218012Smckusick while ((to = rmtopen(tape, 2)) < 0) 28318012Smckusick #else 28418012Smckusick while ((to = pipeout ? 1 : creat(tape, 0666)) < 0) 28518012Smckusick #endif 28618012Smckusick if (!query("Cannot open tape. Do you want to retry the open?")) 28718012Smckusick dumpabort(); 2881425Sroot 28918012Smckusick enslave(); /* Share open tape file descriptor with slaves */ 29018012Smckusick 2911425Sroot asize = 0; 2921425Sroot tapeno++; /* current tape sequence */ 2931425Sroot newtape++; /* new tape signal */ 2941425Sroot spcl.c_volume++; 2951425Sroot spcl.c_type = TS_TAPE; 2961425Sroot spclrec(); 2971425Sroot if (tapeno > 1) 2981425Sroot msg("Tape %d begins with blocks from ino %d\n", 2991425Sroot tapeno, ino); 3001425Sroot } 3011425Sroot } 3021425Sroot 3031425Sroot dumpabort() 3041425Sroot { 30518012Smckusick if (master != 0 && master != getpid()) 30618012Smckusick kill(master, SIGIOT); 3071925Swnj msg("The ENTIRE dump is aborted.\n"); 3081425Sroot Exit(X_ABORT); 3091425Sroot } 3101425Sroot 3111425Sroot Exit(status) 3121425Sroot { 3131425Sroot #ifdef TDEBUG 3141425Sroot msg("pid = %d exits with status %d\n", getpid(), status); 3151425Sroot #endif TDEBUG 3161925Swnj exit(status); 3171425Sroot } 31818012Smckusick 31918012Smckusick #define OK 020 32018012Smckusick char tok = OK; 32118012Smckusick 32218012Smckusick enslave() 32318012Smckusick { 32418012Smckusick int prev[2], next[2], cmd[2]; /* file descriptors for pipes */ 32519982Smckusick int i, j, ret, slavepid; 32618012Smckusick 32718012Smckusick master = getpid(); 32818012Smckusick signal(SIGPIPE, dumpabort); 32918012Smckusick signal(SIGIOT, tperror); /* SIGIOT asks for restart from checkpoint */ 33018012Smckusick pipe(prev); 33118012Smckusick for (i = rotor = 0; i < SLAVES; ++i) { 33218012Smckusick if ((i < SLAVES - 1 && pipe(next) < 0) || pipe(cmd) < 0 33318012Smckusick || (slavepid = fork()) < 0) { 33418012Smckusick perror(" DUMP: too many slaves"); 33518012Smckusick dumpabort(); 33618012Smckusick } 33718012Smckusick if (i >= SLAVES - 1) 33818012Smckusick next[1] = prev[1]; /* Last slave loops back */ 33918012Smckusick slavefd[i] = cmd[1]; 34018012Smckusick if (slavepid == 0) { /* Slave starts up here */ 34118012Smckusick for (j = 0; j <= i; j++) 34218012Smckusick close(slavefd[j]); 34318012Smckusick if (i < SLAVES - 1) { 34418012Smckusick close(prev[1]); 34518012Smckusick close(next[0]); 34618012Smckusick } else { /* Insert initial token */ 34719982Smckusick if ((ret = write(next[1], &tok, 1)) != 1) 34819982Smckusick ringerr(ret, "cannot start token"); 34918012Smckusick } 35018012Smckusick doslave(i, cmd[0], prev[0], next[1]); 35118012Smckusick close(next[1]); 35218012Smckusick j = read(prev[0], &tok, 1); /* Eat the final token */ 35318012Smckusick #ifdef RDUMP /* Read remaining acknowledges */ 35418012Smckusick for (; j > 0 && (tok &~ OK) > 0; tok--) { 35518012Smckusick if (rmtwrite2() != writesize && (tok & OK)) { 35618012Smckusick kill(master, SIGIOT); 35718012Smckusick tok &= ~OK; 35818012Smckusick } 35918012Smckusick } 36018012Smckusick #endif 36118012Smckusick Exit(X_FINOK); 36218012Smckusick } 36318012Smckusick close(cmd[0]); 36418012Smckusick close(next[1]); 36518012Smckusick close(prev[0]); 36618012Smckusick prev[0] = next[0]; 36718012Smckusick } 36818012Smckusick master = 0; 36918012Smckusick } 37018012Smckusick 37118012Smckusick /* 37218012Smckusick * Somebody must have died, should never happen 37318012Smckusick */ 37419982Smckusick ringerr(code, msg, a1, a2) 37519982Smckusick int code; 37619982Smckusick char *msg; 37719982Smckusick int a1, a2; 37818012Smckusick { 37919982Smckusick char buf[BUFSIZ]; 38019982Smckusick 38119982Smckusick fprintf(stderr, " DUMP: "); 38219982Smckusick sprintf(buf, msg, a1, a2); 38319982Smckusick if (code < 0) 38419982Smckusick perror(msg); 38519982Smckusick else if (code == 0) 38619982Smckusick fprintf(stderr, "%s: unexpected EOF\n", buf); 38719982Smckusick else 38819982Smckusick fprintf(stderr, "%s: code %d\n", buf, code); 38918012Smckusick kill(master, SIGPIPE); 39018012Smckusick Exit(X_ABORT); 39118012Smckusick } 39218012Smckusick 39319982Smckusick int childnum; 39419982Smckusick sigpipe() 39519982Smckusick { 39619982Smckusick 39719982Smckusick ringerr(childnum, "SIGPIPE raised"); 39819982Smckusick } 39919982Smckusick 40018012Smckusick doslave(num, cmd, prev, next) 40118012Smckusick int num, cmd, prev, next; 40218012Smckusick { 40319982Smckusick int ret; 40419982Smckusick 40518012Smckusick tmsg("slave %d\n", num); 40618012Smckusick signal(SIGINT, SIG_IGN); /* Master handles it */ 40718012Smckusick signal(SIGTERM, SIG_IGN); 40819982Smckusick signal(SIGPIPE, sigpipe); 40919982Smckusick childnum = num; 41018012Smckusick close(fi); 41118012Smckusick if ((fi = open(disk, 0)) < 0) { /* Need our own seek pointer */ 41218012Smckusick perror(" DUMP: can't reopen disk"); 41318012Smckusick kill(master, SIGPIPE); 41418012Smckusick Exit(X_ABORT); 41518012Smckusick } 41619982Smckusick while ((ret = readpipe(cmd, req, reqsiz)) == reqsiz) { 41718012Smckusick register struct req *p = req; 41818012Smckusick for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) { 41918012Smckusick if (p->dblk) { 42018012Smckusick tmsg("%d READS %d\n", num, p->count); 42118012Smckusick bread(p->dblk, tblock[trecno], 42218012Smckusick p->count * TP_BSIZE); 42318012Smckusick } else { 42418012Smckusick tmsg("%d PIPEIN %d\n", num, p->count); 42518012Smckusick if (p->count != 1) 42619982Smckusick ringerr(11, "%d PIPEIN %d", num, 42719982Smckusick p->count); 42819947Smckusick if (readpipe(cmd, tblock[trecno], TP_BSIZE) != TP_BSIZE) 42918012Smckusick senderr(); 43018012Smckusick } 43118012Smckusick } 43219982Smckusick if ((ret = read(prev, &tok, 1)) != 1) 43319982Smckusick ringerr(ret, "read token"); /* Wait your turn */ 43418012Smckusick tmsg("%d WRITE\n", num); 43518012Smckusick #ifdef RDUMP 43618012Smckusick if (tok & OK) { 43718012Smckusick rmtwrite0(writesize); 43818012Smckusick rmtwrite1(tblock[0], writesize); 43918012Smckusick tok++; /* Number of writes in progress */ 44018012Smckusick } 44118012Smckusick if (tok > (LAG|OK) && (--tok, rmtwrite2() != writesize)) { 44218012Smckusick #else 44318012Smckusick if ((tok & OK) && 44418012Smckusick write(to, tblock[0], writesize) != writesize) { 44518012Smckusick perror(tape); 44618012Smckusick #endif 44718012Smckusick kill(master, SIGIOT); /* restart from checkpoint */ 44818012Smckusick tok &= ~OK; 44918012Smckusick } 45019982Smckusick if ((ret = write(next, &tok, 1)) != 1) 45119982Smckusick ringerr(ret, "write token"); /* Next slave's turn */ 45218012Smckusick } 45319982Smckusick if (ret != 0) 45419982Smckusick ringerr(ret, "partial record?"); 45518012Smckusick tmsg("%d CLOSE\n", num); 45618012Smckusick } 45719947Smckusick 45819947Smckusick /* 45919947Smckusick * Since a read from a pipe may not return all we asked for 46019947Smckusick * we must loop until we get all we need 46119947Smckusick */ 46219947Smckusick readpipe(fd, buf, cnt) 46319947Smckusick int fd; 46419947Smckusick char *buf; 46519947Smckusick int cnt; 46619947Smckusick { 46719947Smckusick int rd, got; 46819947Smckusick 46919947Smckusick for (rd = cnt; rd > 0; rd -= got) { 47019947Smckusick got = read(fd, buf, rd); 47119947Smckusick if (got < 0) 47219947Smckusick return (got); 47319947Smckusick if (got == 0) 47419947Smckusick return (cnt - rd); 47519947Smckusick buf += got; 47619947Smckusick } 47719947Smckusick return (cnt); 47819947Smckusick } 479