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*24181Smckusick static char sccsid[] = "@(#)tape.c 5.2 (Berkeley) 08/05/85"; 922040Sdist #endif not lint 1017527Ssam 111425Sroot #include "dump.h" 12*24181Smckusick #include <sys/file.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 */ 18*24181Smckusick extern int cartridge; 19*24181Smckusick int tenths; /* length of tape used per block written */ 201425Sroot 2110911Ssam /* 22*24181Smckusick * Concurrent dump mods (Caltech) - disk block reading and tape writing 2318012Smckusick * are exported to several slave processes. While one slave writes the 2418012Smckusick * tape, the others read disk blocks; they pass control of the tape in 25*24181Smckusick * a ring via flock(). The parent process traverses the filesystem and 26*24181Smckusick * sends spclrec()'s and lists of daddr's to each slave via pipes. 27*24181Smckusick * 28*24181Smckusick * from "@(#)dumptape.c 2.1 (Berkeley+Caltech mods) 4/7/85"; 2910911Ssam */ 3018012Smckusick struct req { /* instruction packets sent to slaves */ 3118012Smckusick daddr_t dblk; 3218012Smckusick int count; 3318012Smckusick } *req; 3418012Smckusick int reqsiz; 3518012Smckusick 36*24181Smckusick #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ 37*24181Smckusick int slavepid[SLAVES]; 3818012Smckusick int slavefd[SLAVES]; /* Pipes from master to each slave */ 3918012Smckusick int rotor; /* Current slave number */ 4018012Smckusick int master; /* Pid of master, for sending error signals */ 4118012Smckusick int trace = 0; /* Protocol trace; easily patchable with adb */ 4218012Smckusick #define tmsg if (trace) msg 4318012Smckusick 4418012Smckusick /* 45*24181Smckusick /* Allocate tape buffer contiguous with the array of instruction 46*24181Smckusick * packets, so flusht() can write them together with one write(). 47*24181Smckusick * Align tape buffer on page boundary to speed up tape write(). 4818012Smckusick */ 4910911Ssam alloctape() 5010911Ssam { 5110911Ssam 52*24181Smckusick int pgoff = getpagesize() - 1; 5310911Ssam writesize = ntrec * TP_BSIZE; 54*24181Smckusick /* 55*24181Smckusick * 92185 NEEDS 0.4"; 92181 NEEDS 0.8" to start/stop (see TU80 manual) 56*24181Smckusick */ 57*24181Smckusick tenths = writesize/density + (cartridge ? 16 : density == 625 ? 4 : 8); 58*24181Smckusick 5918012Smckusick reqsiz = ntrec * sizeof(struct req); 60*24181Smckusick req = (struct req *)malloc(reqsiz + writesize + pgoff); 61*24181Smckusick if (req == NULL) 62*24181Smckusick return(0); 63*24181Smckusick tblock = (char (*)[TP_BSIZE]) (((long)&req[ntrec] + pgoff) &~ pgoff); 64*24181Smckusick req = (struct req *)tblock; 65*24181Smckusick req = &req[-ntrec]; /* Cmd packets go in front of tape buffer */ 66*24181Smckusick return(1); 6710911Ssam } 6810911Ssam 6918012Smckusick /* 70*24181Smckusick * Make copy of spclrec, to later send to tape writer. 7118012Smckusick */ 721425Sroot taprec(dp) 735329Smckusic char *dp; 741425Sroot { 751425Sroot 7618012Smckusick tmsg("taprec %d\n", trecno); 7718012Smckusick req[trecno].dblk = (daddr_t)0; 7818012Smckusick req[trecno].count = 1; 79*24181Smckusick *(union u_spcl *)(*tblock++) = *(union u_spcl *)dp; /* movc3 */ 80*24181Smckusick trecno++; 811425Sroot spcl.c_tapea++; 82*24181Smckusick if(trecno >= ntrec) 831425Sroot flusht(); 841425Sroot } 851425Sroot 864774Smckusic dmpblk(blkno, size) 874774Smckusic daddr_t blkno; 884774Smckusic int size; 891425Sroot { 9018012Smckusick int tpblks, dblkno; 9118012Smckusick register int avail; 921425Sroot 935329Smckusic dblkno = fsbtodb(sblock, blkno); 9418012Smckusick tpblks = size / TP_BSIZE; 9518012Smckusick while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { 9618012Smckusick tmsg("dmpblk %d\n", avail); 9718012Smckusick req[trecno].dblk = dblkno; 9818012Smckusick req[trecno].count = avail; 994774Smckusic spcl.c_tapea += avail; 100*24181Smckusick if ((trecno += avail) >= ntrec) 10118012Smckusick flusht(); 1025329Smckusic 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 } 11618012Smckusick msg("Tape write error on tape %d\n", 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"); 123*24181Smckusick killall(); 12418012Smckusick nogripe = 1; 12518012Smckusick close_rewind(); 12618012Smckusick Exit(X_REWRITE); 12718012Smckusick } 12818012Smckusick 12918012Smckusick #ifdef RDUMP 13018012Smckusick tflush(cnt) 13118012Smckusick int cnt; 13218012Smckusick { 13318012Smckusick int i; 13418012Smckusick 13518012Smckusick for (i = 0; i < ntrec; i++) 13618012Smckusick spclrec(); 13718012Smckusick } 13818012Smckusick #endif RDUMP 13918012Smckusick 1401425Sroot flusht() 1411425Sroot { 14218012Smckusick int sig, siz = (char *)tblock - (char *)req; 1431425Sroot 14418012Smckusick tmsg("flusht %d\n", siz); 145*24181Smckusick sig = sigblock(1 << SIGINT-1); /* Don't abort pipe write */ 146*24181Smckusick if (write(slavefd[rotor], req, siz) != siz) { 147*24181Smckusick perror(" DUMP: pipe error in command to slave"); 148*24181Smckusick dumpabort(); 149*24181Smckusick } 15018012Smckusick sigsetmask(sig); 15118012Smckusick if (++rotor >= SLAVES) rotor = 0; 15218012Smckusick tblock = (char (*)[TP_BSIZE]) &req[ntrec]; 1531425Sroot trecno = 0; 154*24181Smckusick asize += tenths; 15510911Ssam blockswritten += ntrec; 15612331Smckusick if (!pipeout && asize > tsize) { 1571425Sroot close_rewind(); 1581425Sroot otape(); 1591425Sroot } 1601425Sroot timeest(); 1611425Sroot } 1621425Sroot 1631425Sroot rewind() 1641425Sroot { 165*24181Smckusick int f; 16612331Smckusick 16712331Smckusick if (pipeout) 16812331Smckusick return; 16918012Smckusick for (f = 0; f < SLAVES; f++) 17018012Smckusick close(slavefd[f]); 17118012Smckusick while (wait(NULL) >= 0) ; /* wait for any signals from slaves */ 17218012Smckusick msg("Tape rewinding\n"); 17318012Smckusick #ifdef RDUMP 17418012Smckusick rmtclose(); 17518012Smckusick while (rmtopen(tape, 0) < 0) 17618012Smckusick sleep(10); 17718012Smckusick rmtclose(); 1781425Sroot #else 1793214Swnj close(to); 1803214Swnj while ((f = open(tape, 0)) < 0) 1813214Swnj sleep (10); 1823214Swnj close(f); 183*24181Smckusick #endif RDUMP 1841425Sroot } 1851425Sroot 1861425Sroot close_rewind() 1871425Sroot { 18818012Smckusick rewind(); 18918012Smckusick if (!nogripe) { 1901425Sroot msg("Change Tapes: Mount tape #%d\n", tapeno+1); 1911425Sroot broadcast("CHANGE TAPES!\7\7\n"); 1921425Sroot } 19318012Smckusick while (!query("Is the new tape mounted and ready to go?")) 19418012Smckusick if (query("Do you want to abort?")) 1951425Sroot dumpabort(); 1961425Sroot } 1971425Sroot 1981425Sroot /* 19918012Smckusick * We implement taking and restoring checkpoints on the tape level. 2001425Sroot * When each tape is opened, a new process is created by forking; this 2011425Sroot * saves all of the necessary context in the parent. The child 2021425Sroot * continues the dump; the parent waits around, saving the context. 2031425Sroot * If the child returns X_REWRITE, then it had problems writing that tape; 2041425Sroot * this causes the parent to fork again, duplicating the context, and 2051425Sroot * everything continues as if nothing had happened. 2061425Sroot */ 2071425Sroot 2081425Sroot otape() 2091425Sroot { 2101425Sroot int parentpid; 2111425Sroot int childpid; 2121425Sroot int status; 2131425Sroot int waitpid; 214*24181Smckusick int (*interrupt)(); 2151425Sroot 2161425Sroot parentpid = getpid(); 2171425Sroot 2181425Sroot restore_check_point: 219*24181Smckusick interrupt = signal(SIGINT, SIG_IGN); 2201425Sroot childpid = fork(); 22118012Smckusick if (childpid < 0) { 2221425Sroot msg("Context save fork fails in parent %d\n", parentpid); 2231425Sroot Exit(X_ABORT); 2241425Sroot } 22518012Smckusick if (childpid != 0) { 2261425Sroot /* 2271425Sroot * PARENT: 2281425Sroot * save the context by waiting 2291425Sroot * until the child doing all of the work returns. 23018012Smckusick * don't catch the interrupt 2311425Sroot */ 2321425Sroot #ifdef TDEBUG 2331425Sroot msg("Tape: %d; parent process: %d child process %d\n", 2341425Sroot tapeno+1, parentpid, childpid); 2351425Sroot #endif TDEBUG 23618012Smckusick while ((waitpid = wait(&status)) != childpid) 23718012Smckusick msg("Parent %d waiting for child %d has another child %d return\n", 23818012Smckusick parentpid, childpid, waitpid); 23918012Smckusick if (status & 0xFF) { 2401425Sroot msg("Child %d returns LOB status %o\n", 2411425Sroot childpid, status&0xFF); 2421425Sroot } 2431425Sroot status = (status >> 8) & 0xFF; 2441425Sroot #ifdef TDEBUG 24518012Smckusick switch(status) { 2461425Sroot case X_FINOK: 2471425Sroot msg("Child %d finishes X_FINOK\n", childpid); 2481425Sroot break; 2491425Sroot case X_ABORT: 2501425Sroot msg("Child %d finishes X_ABORT\n", childpid); 2511425Sroot break; 2521425Sroot case X_REWRITE: 2531425Sroot msg("Child %d finishes X_REWRITE\n", childpid); 2541425Sroot break; 2551425Sroot default: 25618012Smckusick msg("Child %d finishes unknown %d\n", 25718012Smckusick childpid, status); 2581425Sroot break; 2591425Sroot } 2601425Sroot #endif TDEBUG 26118012Smckusick switch(status) { 2621425Sroot case X_FINOK: 2631425Sroot Exit(X_FINOK); 2641425Sroot case X_ABORT: 2651425Sroot Exit(X_ABORT); 2661425Sroot case X_REWRITE: 2671425Sroot goto restore_check_point; 2681425Sroot default: 2691425Sroot msg("Bad return code from dump: %d\n", status); 2701425Sroot Exit(X_ABORT); 2711425Sroot } 2721425Sroot /*NOTREACHED*/ 2731425Sroot } else { /* we are the child; just continue */ 2741425Sroot #ifdef TDEBUG 2751425Sroot sleep(4); /* allow time for parent's message to get out */ 2761425Sroot msg("Child on Tape %d has parent %d, my pid = %d\n", 2771425Sroot tapeno+1, parentpid, getpid()); 2781425Sroot #endif 279*24181Smckusick signal(SIGINT, interrupt); 28018012Smckusick #ifdef RDUMP 28118012Smckusick while ((to = rmtopen(tape, 2)) < 0) 28218012Smckusick #else 28318012Smckusick while ((to = pipeout ? 1 : creat(tape, 0666)) < 0) 284*24181Smckusick #endif RDUMP 28518012Smckusick if (!query("Cannot open tape. Do you want to retry the open?")) 28618012Smckusick dumpabort(); 2871425Sroot 28818012Smckusick enslave(); /* Share open tape file descriptor with slaves */ 28918012Smckusick 2901425Sroot asize = 0; 2911425Sroot tapeno++; /* current tape sequence */ 2921425Sroot newtape++; /* new tape signal */ 2931425Sroot spcl.c_volume++; 2941425Sroot spcl.c_type = TS_TAPE; 2951425Sroot spclrec(); 2961425Sroot if (tapeno > 1) 2971425Sroot msg("Tape %d begins with blocks from ino %d\n", 2981425Sroot tapeno, ino); 2991425Sroot } 3001425Sroot } 3011425Sroot 3021425Sroot dumpabort() 3031425Sroot { 30418012Smckusick if (master != 0 && master != getpid()) 305*24181Smckusick kill(master, SIGPIPE); 306*24181Smckusick else { 307*24181Smckusick killall(); 308*24181Smckusick msg("The ENTIRE dump is aborted.\n"); 309*24181Smckusick } 3101425Sroot Exit(X_ABORT); 3111425Sroot } 3121425Sroot 3131425Sroot Exit(status) 3141425Sroot { 3151425Sroot #ifdef TDEBUG 3161425Sroot msg("pid = %d exits with status %d\n", getpid(), status); 3171425Sroot #endif TDEBUG 3181925Swnj exit(status); 3191425Sroot } 32018012Smckusick 321*24181Smckusick /* 322*24181Smckusick * prefer pipe(), but flock() barfs on them 323*24181Smckusick */ 324*24181Smckusick lockfile(fd) 325*24181Smckusick int fd[2]; 326*24181Smckusick { 327*24181Smckusick char tmpname[20]; 32818012Smckusick 329*24181Smckusick strcpy(tmpname, "/tmp/dumplockXXXXXX"); 330*24181Smckusick mktemp(tmpname); 331*24181Smckusick if ((fd[1] = creat(tmpname, 0400)) < 0) 332*24181Smckusick return(fd[1]); 333*24181Smckusick fd[0] = open(tmpname, 0); 334*24181Smckusick unlink(tmpname); 335*24181Smckusick return (fd[0] < 0 ? fd[0] : 0); 336*24181Smckusick } 337*24181Smckusick 33818012Smckusick enslave() 33918012Smckusick { 340*24181Smckusick int first[2], prev[2], next[2], cmd[2]; /* file descriptors */ 341*24181Smckusick register int i, j; 34218012Smckusick 34318012Smckusick master = getpid(); 344*24181Smckusick signal(SIGPIPE, dumpabort); /* Slave quit/died/killed -> abort */ 345*24181Smckusick signal(SIGIOT, tperror); /* SIGIOT -> restart from checkpoint */ 346*24181Smckusick lockfile(first); 347*24181Smckusick for (i = 0; i < SLAVES; i++) { 348*24181Smckusick if (i == 0) { 349*24181Smckusick prev[0] = first[1]; 350*24181Smckusick prev[1] = first[0]; 351*24181Smckusick } else { 352*24181Smckusick prev[0] = next[0]; 353*24181Smckusick prev[1] = next[1]; 354*24181Smckusick flock(prev[1], LOCK_EX); 355*24181Smckusick } 356*24181Smckusick next[0] = first[0]; 357*24181Smckusick next[1] = first[1]; /* Last slave loops back */ 358*24181Smckusick if ((i < SLAVES-1 && lockfile(next) < 0) || pipe(cmd) < 0 359*24181Smckusick || (slavepid[i] = fork()) < 0) { 360*24181Smckusick perror(" DUMP: too many slaves (recompile smaller)"); 36118012Smckusick dumpabort(); 36218012Smckusick } 36318012Smckusick slavefd[i] = cmd[1]; 364*24181Smckusick if (slavepid[i] == 0) { /* Slave starts up here */ 36518012Smckusick for (j = 0; j <= i; j++) 36618012Smckusick close(slavefd[j]); 367*24181Smckusick signal(SIGINT, SIG_IGN); /* Master handles these */ 368*24181Smckusick signal(SIGTERM, SIG_IGN); 369*24181Smckusick doslave(i, cmd[0], prev, next); 37018012Smckusick Exit(X_FINOK); 37118012Smckusick } 37218012Smckusick close(cmd[0]); 373*24181Smckusick if (i > 0) { 374*24181Smckusick close(prev[0]); 375*24181Smckusick close(prev[1]); 376*24181Smckusick } 37718012Smckusick } 378*24181Smckusick close(first[0]); 379*24181Smckusick close(first[1]); 380*24181Smckusick master = 0; rotor = 0; 38118012Smckusick } 38218012Smckusick 383*24181Smckusick killall() 38418012Smckusick { 385*24181Smckusick register int i; 38619982Smckusick 387*24181Smckusick for (i = 0; i < SLAVES; i++) 388*24181Smckusick if (slavepid[i] > 0) 389*24181Smckusick kill(slavepid[i], SIGKILL); 39018012Smckusick } 39118012Smckusick 392*24181Smckusick /* 393*24181Smckusick * Synchronization - each process has a lockfile, and shares file 394*24181Smckusick * descriptors to the following process's lockfile. When our write 395*24181Smckusick * completes, we release our lock on the following process's lock- 396*24181Smckusick * file, allowing the following process to lock it and proceed. We 397*24181Smckusick * get the lock back for the next cycle by swapping descriptors. 398*24181Smckusick */ 399*24181Smckusick doslave(mynum,cmd,prev,next) 400*24181Smckusick int mynum, cmd, prev[2], next[2]; 40119982Smckusick { 402*24181Smckusick register int toggle = 0, firstdone = mynum; 40319982Smckusick 404*24181Smckusick tmsg("slave %d\n", mynum); 40518012Smckusick close(fi); 40618012Smckusick if ((fi = open(disk, 0)) < 0) { /* Need our own seek pointer */ 407*24181Smckusick perror(" DUMP: slave couldn't reopen disk"); 408*24181Smckusick kill(master, SIGPIPE); /* dumpabort */ 40918012Smckusick Exit(X_ABORT); 41018012Smckusick } 411*24181Smckusick /* 412*24181Smckusick * Get list of blocks to dump 413*24181Smckusick */ 414*24181Smckusick while (readpipe(cmd, req, reqsiz) > 0) { 41518012Smckusick register struct req *p = req; 41618012Smckusick for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) { 41718012Smckusick if (p->dblk) { 418*24181Smckusick tmsg("%d READS %d\n", mynum, p->count); 41918012Smckusick bread(p->dblk, tblock[trecno], 42018012Smckusick p->count * TP_BSIZE); 42118012Smckusick } else { 422*24181Smckusick tmsg("%d PIPEIN %d\n", mynum, p->count); 423*24181Smckusick if (p->count != 1 || 424*24181Smckusick readpipe(cmd, tblock[trecno], TP_BSIZE) <= 0) { 425*24181Smckusick msg("Master/slave protocol botched"); 426*24181Smckusick dumpabort(); 427*24181Smckusick } 42818012Smckusick } 42918012Smckusick } 430*24181Smckusick flock(prev[toggle], LOCK_EX); /* Wait our turn */ 431*24181Smckusick tmsg("%d WRITE\n", mynum); 43218012Smckusick #ifdef RDUMP 433*24181Smckusick #ifndef sun /* Defer checking first write until next one is started */ 434*24181Smckusick rmtwrite0(writesize); 435*24181Smckusick rmtwrite1(tblock[0],writesize); 436*24181Smckusick if (firstdone == 0) firstdone = -1; 437*24181Smckusick else if (rmtwrite2() != writesize) { 438*24181Smckusick rmtwrite2(); /* Don't care if another err */ 43918012Smckusick #else 440*24181Smckusick /* Asynchronous writes can hang Suns; do it synchronously */ 441*24181Smckusick if (rmtwrite(tblock[0],writesize) != writesize) { 442*24181Smckusick #endif sun 443*24181Smckusick #else /* Local tape drive */ 444*24181Smckusick if (write(to,tblock[0],writesize) != writesize) { 44518012Smckusick perror(tape); 446*24181Smckusick #endif RDUMP 44718012Smckusick kill(master, SIGIOT); /* restart from checkpoint */ 448*24181Smckusick for (;;) sigpause(0); 44918012Smckusick } 450*24181Smckusick toggle ^= 1; 451*24181Smckusick flock(next[toggle], LOCK_UN); /* Next slave's turn */ 452*24181Smckusick } /* Also jolts him awake */ 453*24181Smckusick #ifdef RDUMP /* One more time around, to check last write */ 454*24181Smckusick #ifndef sun 455*24181Smckusick flock(prev[toggle], LOCK_EX); 456*24181Smckusick tmsg("%d LAST\n", mynum); 457*24181Smckusick if (firstdone < 0 && rmtwrite2() != writesize) { 458*24181Smckusick kill(master, SIGIOT); 459*24181Smckusick for (;;) 460*24181Smckusick sigpause(0); 46118012Smckusick } 462*24181Smckusick toggle ^= 1; 463*24181Smckusick flock(next[toggle], LOCK_UN); 464*24181Smckusick #endif sun 465*24181Smckusick #endif RDUMP 46618012Smckusick } 46719947Smckusick 46819947Smckusick /* 46919947Smckusick * Since a read from a pipe may not return all we asked for 47019947Smckusick * we must loop until we get all we need 47119947Smckusick */ 47219947Smckusick readpipe(fd, buf, cnt) 47319947Smckusick int fd; 47419947Smckusick char *buf; 47519947Smckusick int cnt; 47619947Smckusick { 47719947Smckusick int rd, got; 47819947Smckusick 47919947Smckusick for (rd = cnt; rd > 0; rd -= got) { 48019947Smckusick got = read(fd, buf, rd); 481*24181Smckusick if (got <= 0) { 482*24181Smckusick if (rd == cnt && got == 0) 483*24181Smckusick return (0); /* Normal EOF */ 484*24181Smckusick msg("short pipe read"); 485*24181Smckusick dumpabort(); 486*24181Smckusick } 48719947Smckusick buf += got; 48819947Smckusick } 48919947Smckusick return (cnt); 49019947Smckusick } 491