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*28644Smckusick static char sccsid[] = "@(#)tape.c 5.5 (Berkeley) 05/23/86"; 922040Sdist #endif not lint 1017527Ssam 1125219Smckusick #include <sys/file.h> 121425Sroot #include "dump.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; 1725219Smckusick extern int ntrec; /* blocking factor on tape */ 1825219Smckusick extern int cartridge; 1925219Smckusick extern int read(), write(); 2025219Smckusick #ifdef RDUMP 2125219Smckusick extern char *host; 2225219Smckusick #endif RDUMP 231425Sroot 2410911Ssam /* 2524181Smckusick * Concurrent dump mods (Caltech) - disk block reading and tape writing 2618012Smckusick * are exported to several slave processes. While one slave writes the 2718012Smckusick * tape, the others read disk blocks; they pass control of the tape in 2824181Smckusick * a ring via flock(). The parent process traverses the filesystem and 2925219Smckusick * sends spclrec()'s and lists of daddr's to the slaves via pipes. 3010911Ssam */ 3118012Smckusick struct req { /* instruction packets sent to slaves */ 3218012Smckusick daddr_t dblk; 3318012Smckusick int count; 3418012Smckusick } *req; 3518012Smckusick int reqsiz; 3618012Smckusick 3724181Smckusick #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ 3825219Smckusick int slavefd[SLAVES]; /* pipes from master to each slave */ 3925219Smckusick int slavepid[SLAVES]; /* used by killall() */ 4025219Smckusick int rotor; /* next slave to be instructed */ 4125219Smckusick int master; /* pid of master, for sending error signals */ 4225219Smckusick int tenths; /* length of tape used per block written */ 4318012Smckusick 4410911Ssam alloctape() 4510911Ssam { 4625219Smckusick int pgoff = getpagesize() - 1; 4710911Ssam 4810911Ssam writesize = ntrec * TP_BSIZE; 4925219Smckusick reqsiz = ntrec * sizeof(struct req); 5024181Smckusick /* 5125219Smckusick * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode 5225219Smckusick * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require 5325219Smckusick * repositioning after stopping, i.e, streaming mode, where the gap is 5425219Smckusick * variable, 0.30" to 0.45". The gap is maximal when the tape stops. 5524181Smckusick */ 5625219Smckusick tenths = writesize/density + (cartridge ? 16 : density == 625 ? 5 : 8); 5725219Smckusick /* 5825219Smckusick * Allocate tape buffer contiguous with the array of instruction 5925219Smckusick * packets, so flusht() can write them together with one write(). 6025219Smckusick * Align tape buffer on page boundary to speed up tape write(). 6125219Smckusick */ 6224181Smckusick req = (struct req *)malloc(reqsiz + writesize + pgoff); 6324181Smckusick if (req == NULL) 6424181Smckusick return(0); 6524181Smckusick tblock = (char (*)[TP_BSIZE]) (((long)&req[ntrec] + pgoff) &~ pgoff); 6625219Smckusick req = (struct req *)tblock - ntrec; 6724181Smckusick return(1); 6810911Ssam } 6910911Ssam 7025219Smckusick 711425Sroot taprec(dp) 725329Smckusic char *dp; 731425Sroot { 7418012Smckusick req[trecno].dblk = (daddr_t)0; 7518012Smckusick req[trecno].count = 1; 7624181Smckusick *(union u_spcl *)(*tblock++) = *(union u_spcl *)dp; /* movc3 */ 7724181Smckusick trecno++; 781425Sroot spcl.c_tapea++; 7924181Smckusick if(trecno >= ntrec) 801425Sroot flusht(); 811425Sroot } 821425Sroot 834774Smckusic dmpblk(blkno, size) 844774Smckusic daddr_t blkno; 854774Smckusic int size; 861425Sroot { 8725219Smckusick int avail, tpblks, dblkno; 881425Sroot 895329Smckusic dblkno = fsbtodb(sblock, blkno); 9018012Smckusick tpblks = size / TP_BSIZE; 9118012Smckusick while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { 9218012Smckusick req[trecno].dblk = dblkno; 9318012Smckusick req[trecno].count = avail; 9425219Smckusick trecno += avail; 954774Smckusic spcl.c_tapea += avail; 9625219Smckusick if (trecno >= ntrec) 9718012Smckusick flusht(); 985329Smckusic dblkno += avail * (TP_BSIZE / DEV_BSIZE); 995329Smckusic tpblks -= avail; 1004774Smckusic } 1011425Sroot } 1021425Sroot 1031425Sroot int nogripe = 0; 1041425Sroot 10518012Smckusick tperror() { 10618012Smckusick if (pipeout) { 10718012Smckusick msg("Tape write error on %s\n", tape); 10818012Smckusick msg("Cannot recover\n"); 10918012Smckusick dumpabort(); 11018012Smckusick /* NOTREACHED */ 11118012Smckusick } 11225219Smckusick msg("Tape write error %d feet into tape %d\n", asize/120L, tapeno); 11318012Smckusick broadcast("TAPE ERROR!\n"); 11418012Smckusick if (!query("Do you want to restart?")) 11518012Smckusick dumpabort(); 11618012Smckusick msg("This tape will rewind. After it is rewound,\n"); 11718012Smckusick msg("replace the faulty tape with a new one;\n"); 11818012Smckusick msg("this dump volume will be rewritten.\n"); 11924181Smckusick killall(); 12018012Smckusick nogripe = 1; 12118012Smckusick close_rewind(); 12218012Smckusick Exit(X_REWRITE); 12318012Smckusick } 12418012Smckusick 12525219Smckusick sigpipe() 12625219Smckusick { 12725219Smckusick 12825219Smckusick msg("Broken pipe\n"); 12925219Smckusick dumpabort(); 13025219Smckusick } 13125219Smckusick 13218012Smckusick #ifdef RDUMP 13325219Smckusick /* 13425219Smckusick * compatibility routine 13525219Smckusick */ 13625219Smckusick tflush(i) 13725219Smckusick int i; 13818012Smckusick { 13918012Smckusick 14018012Smckusick for (i = 0; i < ntrec; i++) 14118012Smckusick spclrec(); 14218012Smckusick } 14318012Smckusick #endif RDUMP 14418012Smckusick 1451425Sroot flusht() 1461425Sroot { 14725219Smckusick int siz = (char *)tblock - (char *)req; 1481425Sroot 14925219Smckusick if (atomic(write, slavefd[rotor], req, siz) != siz) { 15025219Smckusick perror(" DUMP: error writing command pipe"); 15124181Smckusick dumpabort(); 15224181Smckusick } 15318012Smckusick if (++rotor >= SLAVES) rotor = 0; 15418012Smckusick tblock = (char (*)[TP_BSIZE]) &req[ntrec]; 1551425Sroot trecno = 0; 15624181Smckusick asize += tenths; 15710911Ssam blockswritten += ntrec; 15812331Smckusick if (!pipeout && asize > tsize) { 1591425Sroot close_rewind(); 1601425Sroot otape(); 1611425Sroot } 1621425Sroot timeest(); 1631425Sroot } 1641425Sroot 1651425Sroot rewind() 1661425Sroot { 16724181Smckusick int f; 16812331Smckusick 16912331Smckusick if (pipeout) 17012331Smckusick return; 17118012Smckusick for (f = 0; f < SLAVES; f++) 17218012Smckusick close(slavefd[f]); 17318012Smckusick while (wait(NULL) >= 0) ; /* wait for any signals from slaves */ 17418012Smckusick msg("Tape rewinding\n"); 17518012Smckusick #ifdef RDUMP 17625219Smckusick if (host) { 17725219Smckusick rmtclose(); 17825219Smckusick while (rmtopen(tape, 0) < 0) 17925219Smckusick sleep(10); 18025219Smckusick rmtclose(); 18125219Smckusick return; 18225219Smckusick } 18325219Smckusick #endif RDUMP 1843214Swnj close(to); 1853214Swnj while ((f = open(tape, 0)) < 0) 1863214Swnj sleep (10); 1873214Swnj close(f); 1881425Sroot } 1891425Sroot 1901425Sroot close_rewind() 1911425Sroot { 19218012Smckusick rewind(); 19318012Smckusick if (!nogripe) { 1941425Sroot msg("Change Tapes: Mount tape #%d\n", tapeno+1); 1951425Sroot broadcast("CHANGE TAPES!\7\7\n"); 1961425Sroot } 19718012Smckusick while (!query("Is the new tape mounted and ready to go?")) 19825219Smckusick if (query("Do you want to abort?")) { 1991425Sroot dumpabort(); 20025219Smckusick /*NOTREACHED*/ 20125219Smckusick } 2021425Sroot } 2031425Sroot 2041425Sroot /* 20518012Smckusick * We implement taking and restoring checkpoints on the tape level. 2061425Sroot * When each tape is opened, a new process is created by forking; this 2071425Sroot * saves all of the necessary context in the parent. The child 2081425Sroot * continues the dump; the parent waits around, saving the context. 2091425Sroot * If the child returns X_REWRITE, then it had problems writing that tape; 2101425Sroot * this causes the parent to fork again, duplicating the context, and 2111425Sroot * everything continues as if nothing had happened. 2121425Sroot */ 2131425Sroot 2141425Sroot otape() 2151425Sroot { 2161425Sroot int parentpid; 2171425Sroot int childpid; 2181425Sroot int status; 2191425Sroot int waitpid; 22025219Smckusick int (*interrupt)() = signal(SIGINT, SIG_IGN); 2211425Sroot 2221425Sroot parentpid = getpid(); 2231425Sroot 2241425Sroot restore_check_point: 22525219Smckusick signal(SIGINT, interrupt); 22625219Smckusick /* 22725219Smckusick * All signals are inherited... 22825219Smckusick */ 2291425Sroot childpid = fork(); 23018012Smckusick if (childpid < 0) { 2311425Sroot msg("Context save fork fails in parent %d\n", parentpid); 2321425Sroot Exit(X_ABORT); 2331425Sroot } 23418012Smckusick if (childpid != 0) { 2351425Sroot /* 2361425Sroot * PARENT: 2371425Sroot * save the context by waiting 2381425Sroot * until the child doing all of the work returns. 23918012Smckusick * don't catch the interrupt 2401425Sroot */ 24125219Smckusick signal(SIGINT, SIG_IGN); 2421425Sroot #ifdef TDEBUG 2431425Sroot msg("Tape: %d; parent process: %d child process %d\n", 2441425Sroot tapeno+1, parentpid, childpid); 2451425Sroot #endif TDEBUG 24618012Smckusick while ((waitpid = wait(&status)) != childpid) 24718012Smckusick msg("Parent %d waiting for child %d has another child %d return\n", 24818012Smckusick parentpid, childpid, waitpid); 24918012Smckusick if (status & 0xFF) { 2501425Sroot msg("Child %d returns LOB status %o\n", 2511425Sroot childpid, status&0xFF); 2521425Sroot } 2531425Sroot status = (status >> 8) & 0xFF; 2541425Sroot #ifdef TDEBUG 25518012Smckusick switch(status) { 2561425Sroot case X_FINOK: 2571425Sroot msg("Child %d finishes X_FINOK\n", childpid); 2581425Sroot break; 2591425Sroot case X_ABORT: 2601425Sroot msg("Child %d finishes X_ABORT\n", childpid); 2611425Sroot break; 2621425Sroot case X_REWRITE: 2631425Sroot msg("Child %d finishes X_REWRITE\n", childpid); 2641425Sroot break; 2651425Sroot default: 26618012Smckusick msg("Child %d finishes unknown %d\n", 26725219Smckusick childpid, status); 2681425Sroot break; 2691425Sroot } 2701425Sroot #endif TDEBUG 27118012Smckusick switch(status) { 2721425Sroot case X_FINOK: 2731425Sroot Exit(X_FINOK); 2741425Sroot case X_ABORT: 2751425Sroot Exit(X_ABORT); 2761425Sroot case X_REWRITE: 2771425Sroot goto restore_check_point; 2781425Sroot default: 2791425Sroot msg("Bad return code from dump: %d\n", status); 2801425Sroot Exit(X_ABORT); 2811425Sroot } 2821425Sroot /*NOTREACHED*/ 2831425Sroot } else { /* we are the child; just continue */ 2841425Sroot #ifdef TDEBUG 2851425Sroot sleep(4); /* allow time for parent's message to get out */ 2861425Sroot msg("Child on Tape %d has parent %d, my pid = %d\n", 2871425Sroot tapeno+1, parentpid, getpid()); 28825219Smckusick #endif TDEBUG 28918012Smckusick #ifdef RDUMP 29025219Smckusick while ((to = (host ? rmtopen(tape, 2) : 29125219Smckusick pipeout ? 1 : creat(tape, 0666))) < 0) 29225219Smckusick #else RDUMP 29318012Smckusick while ((to = pipeout ? 1 : creat(tape, 0666)) < 0) 29424181Smckusick #endif RDUMP 29518012Smckusick if (!query("Cannot open tape. Do you want to retry the open?")) 29618012Smckusick dumpabort(); 2971425Sroot 29818012Smckusick enslave(); /* Share open tape file descriptor with slaves */ 29918012Smckusick 3001425Sroot asize = 0; 3011425Sroot tapeno++; /* current tape sequence */ 3021425Sroot newtape++; /* new tape signal */ 3031425Sroot spcl.c_volume++; 3041425Sroot spcl.c_type = TS_TAPE; 3051425Sroot spclrec(); 3061425Sroot if (tapeno > 1) 3071425Sroot msg("Tape %d begins with blocks from ino %d\n", 3081425Sroot tapeno, ino); 3091425Sroot } 3101425Sroot } 3111425Sroot 3121425Sroot dumpabort() 3131425Sroot { 31418012Smckusick if (master != 0 && master != getpid()) 31525219Smckusick kill(master, SIGTERM); /* Signals master to call dumpabort */ 31624181Smckusick else { 31724181Smckusick killall(); 31824181Smckusick msg("The ENTIRE dump is aborted.\n"); 31924181Smckusick } 3201425Sroot Exit(X_ABORT); 3211425Sroot } 3221425Sroot 3231425Sroot Exit(status) 3241425Sroot { 3251425Sroot #ifdef TDEBUG 3261425Sroot msg("pid = %d exits with status %d\n", getpid(), status); 3271425Sroot #endif TDEBUG 3281925Swnj exit(status); 3291425Sroot } 33018012Smckusick 33124181Smckusick /* 33225219Smckusick * could use pipe() for this if flock() worked on pipes 33324181Smckusick */ 33424181Smckusick lockfile(fd) 33524181Smckusick int fd[2]; 33624181Smckusick { 33724181Smckusick char tmpname[20]; 33818012Smckusick 33924181Smckusick strcpy(tmpname, "/tmp/dumplockXXXXXX"); 34024181Smckusick mktemp(tmpname); 34126485Smckusick if ((fd[1] = creat(tmpname, 0400)) < 0) { 34226485Smckusick msg("Could not create lockfile "); 34326485Smckusick perror(tmpname); 34426485Smckusick dumpabort(); 34526485Smckusick } 34626485Smckusick if ((fd[0] = open(tmpname, 0)) < 0) { 34726485Smckusick msg("Could not reopen lockfile "); 34826485Smckusick perror(tmpname); 34926485Smckusick dumpabort(); 35026485Smckusick } 35124181Smckusick unlink(tmpname); 35224181Smckusick } 35324181Smckusick 35418012Smckusick enslave() 35518012Smckusick { 35624181Smckusick int first[2], prev[2], next[2], cmd[2]; /* file descriptors */ 35724181Smckusick register int i, j; 35818012Smckusick 35918012Smckusick master = getpid(); 36025219Smckusick signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ 36125219Smckusick signal(SIGPIPE, sigpipe); 36225219Smckusick signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ 36324181Smckusick lockfile(first); 36424181Smckusick for (i = 0; i < SLAVES; i++) { 36524181Smckusick if (i == 0) { 36624181Smckusick prev[0] = first[1]; 36724181Smckusick prev[1] = first[0]; 36824181Smckusick } else { 36924181Smckusick prev[0] = next[0]; 37024181Smckusick prev[1] = next[1]; 37124181Smckusick flock(prev[1], LOCK_EX); 37224181Smckusick } 37326485Smckusick if (i < SLAVES - 1) { 37426485Smckusick lockfile(next); 37526485Smckusick } else { 37626485Smckusick next[0] = first[0]; 37726485Smckusick next[1] = first[1]; /* Last slave loops back */ 37826485Smckusick } 37926485Smckusick if (pipe(cmd) < 0 || (slavepid[i] = fork()) < 0) { 38026485Smckusick msg("too many slaves, %d (recompile smaller) ", i); 38126485Smckusick perror(""); 38218012Smckusick dumpabort(); 38318012Smckusick } 38418012Smckusick slavefd[i] = cmd[1]; 38525219Smckusick if (slavepid[i] == 0) { /* Slave starts up here */ 38618012Smckusick for (j = 0; j <= i; j++) 38718012Smckusick close(slavefd[j]); 38825219Smckusick signal(SIGINT, SIG_IGN); /* Master handles this */ 38925219Smckusick doslave(cmd[0], prev, next); 39018012Smckusick Exit(X_FINOK); 39118012Smckusick } 39218012Smckusick close(cmd[0]); 39324181Smckusick if (i > 0) { 39424181Smckusick close(prev[0]); 39524181Smckusick close(prev[1]); 39624181Smckusick } 39718012Smckusick } 39824181Smckusick close(first[0]); 39924181Smckusick close(first[1]); 40024181Smckusick master = 0; rotor = 0; 40118012Smckusick } 40218012Smckusick 40324181Smckusick killall() 40418012Smckusick { 40524181Smckusick register int i; 40619982Smckusick 40724181Smckusick for (i = 0; i < SLAVES; i++) 40824181Smckusick if (slavepid[i] > 0) 40924181Smckusick kill(slavepid[i], SIGKILL); 41018012Smckusick } 41118012Smckusick 41224181Smckusick /* 41324181Smckusick * Synchronization - each process has a lockfile, and shares file 41424181Smckusick * descriptors to the following process's lockfile. When our write 41524181Smckusick * completes, we release our lock on the following process's lock- 41624181Smckusick * file, allowing the following process to lock it and proceed. We 41724181Smckusick * get the lock back for the next cycle by swapping descriptors. 41824181Smckusick */ 41925219Smckusick doslave(cmd, prev, next) 42025219Smckusick register int cmd, prev[2], next[2]; 42119982Smckusick { 42225219Smckusick register int nread, toggle = 0; 42319982Smckusick 42418012Smckusick close(fi); 42525219Smckusick if ((fi = open(disk, 0)) < 0) { /* Need our own seek pointer */ 42624181Smckusick perror(" DUMP: slave couldn't reopen disk"); 42725219Smckusick dumpabort(); 42818012Smckusick } 42924181Smckusick /* 43025219Smckusick * Get list of blocks to dump, read the blocks into tape buffer 43124181Smckusick */ 43225219Smckusick while ((nread = atomic(read, cmd, req, reqsiz)) == reqsiz) { 43318012Smckusick register struct req *p = req; 43418012Smckusick for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) { 43518012Smckusick if (p->dblk) { 43618012Smckusick bread(p->dblk, tblock[trecno], 43725219Smckusick p->count * TP_BSIZE); 43818012Smckusick } else { 43925219Smckusick if (p->count != 1 || atomic(read, cmd, 44025219Smckusick tblock[trecno], TP_BSIZE) != TP_BSIZE) { 441*28644Smckusick msg("Master/slave protocol botched.\n"); 44224181Smckusick dumpabort(); 44324181Smckusick } 44418012Smckusick } 44518012Smckusick } 44624181Smckusick flock(prev[toggle], LOCK_EX); /* Wait our turn */ 44725219Smckusick 44818012Smckusick #ifdef RDUMP 44925219Smckusick if ((host ? rmtwrite(tblock[0], writesize) 45025219Smckusick : write(to, tblock[0], writesize)) != writesize) { 45125219Smckusick #else RDUMP 45225219Smckusick if (write(to, tblock[0], writesize) != writesize) { 45324181Smckusick #endif RDUMP 45425219Smckusick kill(master, SIGUSR1); 45525219Smckusick for (;;) 45625219Smckusick sigpause(0); 45718012Smckusick } 45824181Smckusick toggle ^= 1; 45924181Smckusick flock(next[toggle], LOCK_UN); /* Next slave's turn */ 46024181Smckusick } /* Also jolts him awake */ 46125219Smckusick if (nread != 0) { 46225219Smckusick perror(" DUMP: error reading command pipe"); 46325219Smckusick dumpabort(); 46418012Smckusick } 46518012Smckusick } 46619947Smckusick 46719947Smckusick /* 46825219Smckusick * Since a read from a pipe may not return all we asked for, 46925219Smckusick * or a write may not write all we ask if we get a signal, 47025219Smckusick * loop until the count is satisfied (or error). 47119947Smckusick */ 47225219Smckusick atomic(func, fd, buf, count) 47325219Smckusick int (*func)(), fd, count; 47419947Smckusick char *buf; 47519947Smckusick { 47625219Smckusick int got, need = count; 47719947Smckusick 47825219Smckusick while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) 47919947Smckusick buf += got; 48025219Smckusick return (got < 0 ? got : count - need); 48119947Smckusick } 482