122040Sdist /* 222040Sdist * Copyright (c) 1980 Regents of the University of California. 322040Sdist * All rights reserved. The Berkeley software License Agreement 422040Sdist * specifies the terms and conditions for redistribution. 522040Sdist */ 622040Sdist 717527Ssam #ifndef lint 8*46239Storek static char sccsid[] = "@(#)tape.c 5.11 (Berkeley) 02/03/91"; 922040Sdist #endif not lint 1017527Ssam 11*46239Storek #include <sys/types.h> 12*46239Storek #include <fcntl.h> 131425Sroot #include "dump.h" 1439128Smckusick #include "pathnames.h" 151425Sroot 1629899Smckusick char (*tblock)[TP_BSIZE]; /* pointer to malloc()ed buffer for tape */ 1729899Smckusick int writesize; /* size of malloc()ed buffer for tape */ 1829899Smckusick long lastspclrec = -1; /* tape block number of last written header */ 1929899Smckusick int trecno = 0; /* next record to write in current block */ 2025219Smckusick extern int ntrec; /* blocking factor on tape */ 2125219Smckusick extern int cartridge; 2225219Smckusick extern int read(), write(); 2325219Smckusick #ifdef RDUMP 2425219Smckusick extern char *host; 2525219Smckusick #endif RDUMP 261425Sroot 2710911Ssam /* 2824181Smckusick * Concurrent dump mods (Caltech) - disk block reading and tape writing 2918012Smckusick * are exported to several slave processes. While one slave writes the 3018012Smckusick * tape, the others read disk blocks; they pass control of the tape in 3124181Smckusick * a ring via flock(). The parent process traverses the filesystem and 3225219Smckusick * sends spclrec()'s and lists of daddr's to the slaves via pipes. 3310911Ssam */ 3418012Smckusick struct req { /* instruction packets sent to slaves */ 3518012Smckusick daddr_t dblk; 3618012Smckusick int count; 3718012Smckusick } *req; 3818012Smckusick int reqsiz; 3918012Smckusick 4024181Smckusick #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ 4125219Smckusick int slavefd[SLAVES]; /* pipes from master to each slave */ 4225219Smckusick int slavepid[SLAVES]; /* used by killall() */ 4325219Smckusick int rotor; /* next slave to be instructed */ 4425219Smckusick int master; /* pid of master, for sending error signals */ 4525219Smckusick int tenths; /* length of tape used per block written */ 4618012Smckusick 4710911Ssam alloctape() 4810911Ssam { 4925219Smckusick int pgoff = getpagesize() - 1; 5010911Ssam 5110911Ssam writesize = ntrec * TP_BSIZE; 5225219Smckusick reqsiz = ntrec * sizeof(struct req); 5324181Smckusick /* 5425219Smckusick * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode 5525219Smckusick * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require 5625219Smckusick * repositioning after stopping, i.e, streaming mode, where the gap is 5725219Smckusick * variable, 0.30" to 0.45". The gap is maximal when the tape stops. 5824181Smckusick */ 5925219Smckusick tenths = writesize/density + (cartridge ? 16 : density == 625 ? 5 : 8); 6025219Smckusick /* 6125219Smckusick * Allocate tape buffer contiguous with the array of instruction 6225219Smckusick * packets, so flusht() can write them together with one write(). 6325219Smckusick * Align tape buffer on page boundary to speed up tape write(). 6425219Smckusick */ 6524181Smckusick req = (struct req *)malloc(reqsiz + writesize + pgoff); 6624181Smckusick if (req == NULL) 6724181Smckusick return(0); 6824181Smckusick tblock = (char (*)[TP_BSIZE]) (((long)&req[ntrec] + pgoff) &~ pgoff); 6925219Smckusick req = (struct req *)tblock - ntrec; 7024181Smckusick return(1); 7110911Ssam } 7210911Ssam 7325219Smckusick 741425Sroot taprec(dp) 755329Smckusic char *dp; 761425Sroot { 7718012Smckusick req[trecno].dblk = (daddr_t)0; 7818012Smckusick req[trecno].count = 1; 7924181Smckusick *(union u_spcl *)(*tblock++) = *(union u_spcl *)dp; /* movc3 */ 8029899Smckusick lastspclrec = spcl.c_tapea; 8124181Smckusick trecno++; 821425Sroot spcl.c_tapea++; 8324181Smckusick if(trecno >= ntrec) 841425Sroot flusht(); 851425Sroot } 861425Sroot 874774Smckusic dmpblk(blkno, size) 884774Smckusic daddr_t blkno; 894774Smckusic int size; 901425Sroot { 9125219Smckusick int avail, tpblks, dblkno; 921425Sroot 935329Smckusic dblkno = fsbtodb(sblock, blkno); 9418012Smckusick tpblks = size / TP_BSIZE; 9518012Smckusick while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { 9618012Smckusick req[trecno].dblk = dblkno; 9718012Smckusick req[trecno].count = avail; 9825219Smckusick trecno += avail; 994774Smckusic spcl.c_tapea += avail; 10025219Smckusick if (trecno >= ntrec) 10118012Smckusick flusht(); 10230560Smckusick dblkno += avail * (TP_BSIZE / dev_bsize); 1035329Smckusic tpblks -= avail; 1044774Smckusic } 1051425Sroot } 1061425Sroot 1071425Sroot int nogripe = 0; 1081425Sroot 10918012Smckusick tperror() { 11018012Smckusick if (pipeout) { 11118012Smckusick msg("Tape write error on %s\n", tape); 11218012Smckusick msg("Cannot recover\n"); 11318012Smckusick dumpabort(); 11418012Smckusick /* NOTREACHED */ 11518012Smckusick } 11625219Smckusick msg("Tape write error %d feet into tape %d\n", asize/120L, tapeno); 11718012Smckusick broadcast("TAPE ERROR!\n"); 11818012Smckusick if (!query("Do you want to restart?")) 11918012Smckusick dumpabort(); 12018012Smckusick msg("This tape will rewind. After it is rewound,\n"); 12118012Smckusick msg("replace the faulty tape with a new one;\n"); 12218012Smckusick msg("this dump volume will be rewritten.\n"); 12324181Smckusick killall(); 12418012Smckusick nogripe = 1; 12518012Smckusick close_rewind(); 12618012Smckusick Exit(X_REWRITE); 12718012Smckusick } 12818012Smckusick 12925219Smckusick sigpipe() 13025219Smckusick { 13125219Smckusick 13225219Smckusick msg("Broken pipe\n"); 13325219Smckusick dumpabort(); 13425219Smckusick } 13525219Smckusick 13618012Smckusick #ifdef RDUMP 13725219Smckusick /* 13825219Smckusick * compatibility routine 13925219Smckusick */ 14025219Smckusick tflush(i) 14125219Smckusick int i; 14218012Smckusick { 14318012Smckusick 14418012Smckusick for (i = 0; i < ntrec; i++) 14518012Smckusick spclrec(); 14618012Smckusick } 14718012Smckusick #endif RDUMP 14818012Smckusick 1491425Sroot flusht() 1501425Sroot { 15125219Smckusick int siz = (char *)tblock - (char *)req; 1521425Sroot 15325219Smckusick if (atomic(write, slavefd[rotor], req, siz) != siz) { 15425219Smckusick perror(" DUMP: error writing command pipe"); 15524181Smckusick dumpabort(); 15624181Smckusick } 15718012Smckusick if (++rotor >= SLAVES) rotor = 0; 15818012Smckusick tblock = (char (*)[TP_BSIZE]) &req[ntrec]; 1591425Sroot trecno = 0; 16024181Smckusick asize += tenths; 16110911Ssam blockswritten += ntrec; 16212331Smckusick if (!pipeout && asize > tsize) { 1631425Sroot close_rewind(); 1641425Sroot otape(); 1651425Sroot } 1661425Sroot timeest(); 1671425Sroot } 1681425Sroot 169*46239Storek trewind() 1701425Sroot { 17124181Smckusick int f; 17212331Smckusick 17312331Smckusick if (pipeout) 17412331Smckusick return; 17518012Smckusick for (f = 0; f < SLAVES; f++) 17618012Smckusick close(slavefd[f]); 17718012Smckusick while (wait(NULL) >= 0) ; /* wait for any signals from slaves */ 17818012Smckusick msg("Tape rewinding\n"); 17918012Smckusick #ifdef RDUMP 18025219Smckusick if (host) { 18125219Smckusick rmtclose(); 18225219Smckusick while (rmtopen(tape, 0) < 0) 18325219Smckusick sleep(10); 18425219Smckusick rmtclose(); 18525219Smckusick return; 18625219Smckusick } 18725219Smckusick #endif RDUMP 1883214Swnj close(to); 1893214Swnj while ((f = open(tape, 0)) < 0) 1903214Swnj sleep (10); 1913214Swnj close(f); 1921425Sroot } 1931425Sroot 1941425Sroot close_rewind() 1951425Sroot { 196*46239Storek trewind(); 19718012Smckusick if (!nogripe) { 1981425Sroot msg("Change Tapes: Mount tape #%d\n", tapeno+1); 1991425Sroot broadcast("CHANGE TAPES!\7\7\n"); 2001425Sroot } 20118012Smckusick while (!query("Is the new tape mounted and ready to go?")) 20225219Smckusick if (query("Do you want to abort?")) { 2031425Sroot dumpabort(); 20425219Smckusick /*NOTREACHED*/ 20525219Smckusick } 2061425Sroot } 2071425Sroot 2081425Sroot /* 20918012Smckusick * We implement taking and restoring checkpoints on the tape level. 2101425Sroot * When each tape is opened, a new process is created by forking; this 2111425Sroot * saves all of the necessary context in the parent. The child 2121425Sroot * continues the dump; the parent waits around, saving the context. 2131425Sroot * If the child returns X_REWRITE, then it had problems writing that tape; 2141425Sroot * this causes the parent to fork again, duplicating the context, and 2151425Sroot * everything continues as if nothing had happened. 2161425Sroot */ 2171425Sroot 2181425Sroot otape() 2191425Sroot { 2201425Sroot int parentpid; 2211425Sroot int childpid; 2221425Sroot int status; 2231425Sroot int waitpid; 22439164Sbostic sig_t interrupt; 22529899Smckusick int blks, i; 2261425Sroot 22739164Sbostic interrupt = signal(SIGINT, SIG_IGN); 2281425Sroot parentpid = getpid(); 2291425Sroot 2301425Sroot restore_check_point: 23139164Sbostic (void)signal(SIGINT, interrupt); 23225219Smckusick /* 23325219Smckusick * All signals are inherited... 23425219Smckusick */ 2351425Sroot childpid = fork(); 23618012Smckusick if (childpid < 0) { 2371425Sroot msg("Context save fork fails in parent %d\n", parentpid); 2381425Sroot Exit(X_ABORT); 2391425Sroot } 24018012Smckusick if (childpid != 0) { 2411425Sroot /* 2421425Sroot * PARENT: 2431425Sroot * save the context by waiting 2441425Sroot * until the child doing all of the work returns. 24518012Smckusick * don't catch the interrupt 2461425Sroot */ 24725219Smckusick signal(SIGINT, SIG_IGN); 2481425Sroot #ifdef TDEBUG 2491425Sroot msg("Tape: %d; parent process: %d child process %d\n", 2501425Sroot tapeno+1, parentpid, childpid); 2511425Sroot #endif TDEBUG 25218012Smckusick while ((waitpid = wait(&status)) != childpid) 25318012Smckusick msg("Parent %d waiting for child %d has another child %d return\n", 25418012Smckusick parentpid, childpid, waitpid); 25518012Smckusick if (status & 0xFF) { 2561425Sroot msg("Child %d returns LOB status %o\n", 2571425Sroot childpid, status&0xFF); 2581425Sroot } 2591425Sroot status = (status >> 8) & 0xFF; 2601425Sroot #ifdef TDEBUG 26118012Smckusick switch(status) { 2621425Sroot case X_FINOK: 2631425Sroot msg("Child %d finishes X_FINOK\n", childpid); 2641425Sroot break; 2651425Sroot case X_ABORT: 2661425Sroot msg("Child %d finishes X_ABORT\n", childpid); 2671425Sroot break; 2681425Sroot case X_REWRITE: 2691425Sroot msg("Child %d finishes X_REWRITE\n", childpid); 2701425Sroot break; 2711425Sroot default: 27218012Smckusick msg("Child %d finishes unknown %d\n", 27325219Smckusick childpid, status); 2741425Sroot break; 2751425Sroot } 2761425Sroot #endif TDEBUG 27718012Smckusick switch(status) { 2781425Sroot case X_FINOK: 2791425Sroot Exit(X_FINOK); 2801425Sroot case X_ABORT: 2811425Sroot Exit(X_ABORT); 2821425Sroot case X_REWRITE: 2831425Sroot goto restore_check_point; 2841425Sroot default: 2851425Sroot msg("Bad return code from dump: %d\n", status); 2861425Sroot Exit(X_ABORT); 2871425Sroot } 2881425Sroot /*NOTREACHED*/ 2891425Sroot } else { /* we are the child; just continue */ 2901425Sroot #ifdef TDEBUG 2911425Sroot sleep(4); /* allow time for parent's message to get out */ 2921425Sroot msg("Child on Tape %d has parent %d, my pid = %d\n", 2931425Sroot tapeno+1, parentpid, getpid()); 29425219Smckusick #endif TDEBUG 29518012Smckusick #ifdef RDUMP 29625219Smckusick while ((to = (host ? rmtopen(tape, 2) : 29725219Smckusick pipeout ? 1 : creat(tape, 0666))) < 0) 29825219Smckusick #else RDUMP 29918012Smckusick while ((to = pipeout ? 1 : creat(tape, 0666)) < 0) 30024181Smckusick #endif RDUMP 30139128Smckusick { 30239128Smckusick msg("Cannot open tape \"%s\".\n", tape); 30339128Smckusick if (!query("Do you want to retry the open?")) 30418012Smckusick dumpabort(); 30539128Smckusick } 3061425Sroot 30718012Smckusick enslave(); /* Share open tape file descriptor with slaves */ 30818012Smckusick 3091425Sroot asize = 0; 3101425Sroot tapeno++; /* current tape sequence */ 3111425Sroot newtape++; /* new tape signal */ 31229899Smckusick blks = 0; 31329899Smckusick if (spcl.c_type != TS_END) 31429899Smckusick for (i = 0; i < spcl.c_count; i++) 31529899Smckusick if (spcl.c_addr[i] != 0) 31629899Smckusick blks++; 31729899Smckusick spcl.c_count = blks + 1 - spcl.c_tapea + lastspclrec; 3181425Sroot spcl.c_volume++; 3191425Sroot spcl.c_type = TS_TAPE; 32030432Smckusick spcl.c_flags |= DR_NEWHEADER; 3211425Sroot spclrec(); 32230432Smckusick spcl.c_flags &=~ DR_NEWHEADER; 3231425Sroot if (tapeno > 1) 3241425Sroot msg("Tape %d begins with blocks from ino %d\n", 3251425Sroot tapeno, ino); 3261425Sroot } 3271425Sroot } 3281425Sroot 3291425Sroot dumpabort() 3301425Sroot { 33118012Smckusick if (master != 0 && master != getpid()) 33225219Smckusick kill(master, SIGTERM); /* Signals master to call dumpabort */ 33324181Smckusick else { 33424181Smckusick killall(); 33524181Smckusick msg("The ENTIRE dump is aborted.\n"); 33624181Smckusick } 3371425Sroot Exit(X_ABORT); 3381425Sroot } 3391425Sroot 3401425Sroot Exit(status) 341*46239Storek int status; 3421425Sroot { 3431425Sroot #ifdef TDEBUG 3441425Sroot msg("pid = %d exits with status %d\n", getpid(), status); 3451425Sroot #endif TDEBUG 3461925Swnj exit(status); 3471425Sroot } 34818012Smckusick 34924181Smckusick /* 35025219Smckusick * could use pipe() for this if flock() worked on pipes 35124181Smckusick */ 35224181Smckusick lockfile(fd) 35324181Smckusick int fd[2]; 35424181Smckusick { 35524181Smckusick char tmpname[20]; 35618012Smckusick 35739128Smckusick strcpy(tmpname, _PATH_LOCK); 35824181Smckusick mktemp(tmpname); 35926485Smckusick if ((fd[1] = creat(tmpname, 0400)) < 0) { 36026485Smckusick msg("Could not create lockfile "); 36126485Smckusick perror(tmpname); 36226485Smckusick dumpabort(); 36326485Smckusick } 36426485Smckusick if ((fd[0] = open(tmpname, 0)) < 0) { 36526485Smckusick msg("Could not reopen lockfile "); 36626485Smckusick perror(tmpname); 36726485Smckusick dumpabort(); 36826485Smckusick } 36924181Smckusick unlink(tmpname); 37024181Smckusick } 37124181Smckusick 37218012Smckusick enslave() 37318012Smckusick { 37424181Smckusick int first[2], prev[2], next[2], cmd[2]; /* file descriptors */ 37524181Smckusick register int i, j; 37618012Smckusick 37718012Smckusick master = getpid(); 37825219Smckusick signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ 37925219Smckusick signal(SIGPIPE, sigpipe); 38025219Smckusick signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ 38124181Smckusick lockfile(first); 38224181Smckusick for (i = 0; i < SLAVES; i++) { 38324181Smckusick if (i == 0) { 38424181Smckusick prev[0] = first[1]; 38524181Smckusick prev[1] = first[0]; 38624181Smckusick } else { 38724181Smckusick prev[0] = next[0]; 38824181Smckusick prev[1] = next[1]; 38924181Smckusick flock(prev[1], LOCK_EX); 39024181Smckusick } 39126485Smckusick if (i < SLAVES - 1) { 39226485Smckusick lockfile(next); 39326485Smckusick } else { 39426485Smckusick next[0] = first[0]; 39526485Smckusick next[1] = first[1]; /* Last slave loops back */ 39626485Smckusick } 39726485Smckusick if (pipe(cmd) < 0 || (slavepid[i] = fork()) < 0) { 39826485Smckusick msg("too many slaves, %d (recompile smaller) ", i); 39926485Smckusick perror(""); 40018012Smckusick dumpabort(); 40118012Smckusick } 40218012Smckusick slavefd[i] = cmd[1]; 40325219Smckusick if (slavepid[i] == 0) { /* Slave starts up here */ 40418012Smckusick for (j = 0; j <= i; j++) 40518012Smckusick close(slavefd[j]); 40625219Smckusick signal(SIGINT, SIG_IGN); /* Master handles this */ 40725219Smckusick doslave(cmd[0], prev, next); 40818012Smckusick Exit(X_FINOK); 40918012Smckusick } 41018012Smckusick close(cmd[0]); 41124181Smckusick if (i > 0) { 41224181Smckusick close(prev[0]); 41324181Smckusick close(prev[1]); 41424181Smckusick } 41518012Smckusick } 41624181Smckusick close(first[0]); 41724181Smckusick close(first[1]); 41824181Smckusick master = 0; rotor = 0; 41918012Smckusick } 42018012Smckusick 42124181Smckusick killall() 42218012Smckusick { 42324181Smckusick register int i; 42419982Smckusick 42524181Smckusick for (i = 0; i < SLAVES; i++) 42624181Smckusick if (slavepid[i] > 0) 42724181Smckusick kill(slavepid[i], SIGKILL); 42818012Smckusick } 42918012Smckusick 43024181Smckusick /* 43124181Smckusick * Synchronization - each process has a lockfile, and shares file 43224181Smckusick * descriptors to the following process's lockfile. When our write 43324181Smckusick * completes, we release our lock on the following process's lock- 43424181Smckusick * file, allowing the following process to lock it and proceed. We 43524181Smckusick * get the lock back for the next cycle by swapping descriptors. 43624181Smckusick */ 43725219Smckusick doslave(cmd, prev, next) 43825219Smckusick register int cmd, prev[2], next[2]; 43919982Smckusick { 44025219Smckusick register int nread, toggle = 0; 44119982Smckusick 44218012Smckusick close(fi); 44325219Smckusick if ((fi = open(disk, 0)) < 0) { /* Need our own seek pointer */ 44424181Smckusick perror(" DUMP: slave couldn't reopen disk"); 44525219Smckusick dumpabort(); 44618012Smckusick } 44724181Smckusick /* 44825219Smckusick * Get list of blocks to dump, read the blocks into tape buffer 44924181Smckusick */ 45025219Smckusick while ((nread = atomic(read, cmd, req, reqsiz)) == reqsiz) { 45118012Smckusick register struct req *p = req; 45218012Smckusick for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) { 45318012Smckusick if (p->dblk) { 45418012Smckusick bread(p->dblk, tblock[trecno], 45525219Smckusick p->count * TP_BSIZE); 45618012Smckusick } else { 45725219Smckusick if (p->count != 1 || atomic(read, cmd, 45825219Smckusick tblock[trecno], TP_BSIZE) != TP_BSIZE) { 45928644Smckusick msg("Master/slave protocol botched.\n"); 46024181Smckusick dumpabort(); 46124181Smckusick } 46218012Smckusick } 46318012Smckusick } 46424181Smckusick flock(prev[toggle], LOCK_EX); /* Wait our turn */ 46525219Smckusick 46618012Smckusick #ifdef RDUMP 46725219Smckusick if ((host ? rmtwrite(tblock[0], writesize) 46825219Smckusick : write(to, tblock[0], writesize)) != writesize) { 46925219Smckusick #else RDUMP 47025219Smckusick if (write(to, tblock[0], writesize) != writesize) { 47124181Smckusick #endif RDUMP 47225219Smckusick kill(master, SIGUSR1); 47325219Smckusick for (;;) 47425219Smckusick sigpause(0); 47518012Smckusick } 47624181Smckusick toggle ^= 1; 47724181Smckusick flock(next[toggle], LOCK_UN); /* Next slave's turn */ 47824181Smckusick } /* Also jolts him awake */ 47925219Smckusick if (nread != 0) { 48025219Smckusick perror(" DUMP: error reading command pipe"); 48125219Smckusick dumpabort(); 48218012Smckusick } 48318012Smckusick } 48419947Smckusick 48519947Smckusick /* 48625219Smckusick * Since a read from a pipe may not return all we asked for, 48725219Smckusick * or a write may not write all we ask if we get a signal, 48825219Smckusick * loop until the count is satisfied (or error). 48919947Smckusick */ 49025219Smckusick atomic(func, fd, buf, count) 49125219Smckusick int (*func)(), fd, count; 49219947Smckusick char *buf; 49319947Smckusick { 49425219Smckusick int got, need = count; 49519947Smckusick 49625219Smckusick while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) 49719947Smckusick buf += got; 49825219Smckusick return (got < 0 ? got : count - need); 49919947Smckusick } 500