BSD 4_3 release
[unix-history] / usr / src / etc / dump / dumptape.c
index 6a16d69..3031785 100644 (file)
@@ -1,36 +1,79 @@
-static char *sccsid = "@(#)dumptape.c  1.6 (Berkeley) 2/11/83";
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dumptape.c 5.5 (Berkeley) 5/23/86";
+#endif not lint
+
+#include <sys/file.h>
 #include "dump.h"
 
 char   (*tblock)[TP_BSIZE];    /* Pointer to malloc()ed buffer for tape */
 int    writesize;              /* Size of malloc()ed buffer for tape */
 int    trecno = 0;
 extern int ntrec;              /* blocking factor on tape */
 #include "dump.h"
 
 char   (*tblock)[TP_BSIZE];    /* Pointer to malloc()ed buffer for tape */
 int    writesize;              /* Size of malloc()ed buffer for tape */
 int    trecno = 0;
 extern int ntrec;              /* blocking factor on tape */
+extern int cartridge;
+extern int read(), write();
+#ifdef RDUMP
+extern char *host;
+#endif RDUMP
 
 /*
 
 /*
- * Allocate the buffer for tape operations.
- *
- * Depends on global variable ntrec, set from 'b' option in command line.
- * Returns 1 if successful, 0 if failed.
- *
- * For later kernel performance improvement, this buffer should be allocated
- * on a page boundary.
+ * Concurrent dump mods (Caltech) - disk block reading and tape writing
+ * are exported to several slave processes.  While one slave writes the
+ * tape, the others read disk blocks; they pass control of the tape in
+ * a ring via flock(). The parent process traverses the filesystem and
+ * sends spclrec()'s and lists of daddr's to the slaves via pipes.
  */
  */
+struct req {                   /* instruction packets sent to slaves */
+       daddr_t dblk;
+       int count;
+} *req;
+int reqsiz;
+
+#define SLAVES 3               /* 1 slave writing, 1 reading, 1 for slack */
+int slavefd[SLAVES];           /* pipes from master to each slave */
+int slavepid[SLAVES];          /* used by killall() */
+int rotor;                     /* next slave to be instructed */
+int master;                    /* pid of master, for sending error signals */
+int tenths;                    /* length of tape used per block written */
+
 alloctape()
 {
 alloctape()
 {
+       int pgoff = getpagesize() - 1;
 
        writesize = ntrec * TP_BSIZE;
 
        writesize = ntrec * TP_BSIZE;
-       tblock = (char (*)[TP_BSIZE])malloc(writesize);
-       return (tblock != NULL);
+       reqsiz = ntrec * sizeof(struct req);
+       /*
+        * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
+        * (see DEC TU80 User's Guide).  The shorter gaps of 6250-bpi require
+        * repositioning after stopping, i.e, streaming mode, where the gap is
+        * variable, 0.30" to 0.45".  The gap is maximal when the tape stops.
+        */
+       tenths = writesize/density + (cartridge ? 16 : density == 625 ? 5 : 8);
+       /*
+        * Allocate tape buffer contiguous with the array of instruction
+        * packets, so flusht() can write them together with one write().
+        * Align tape buffer on page boundary to speed up tape write().
+        */
+       req = (struct req *)malloc(reqsiz + writesize + pgoff);
+       if (req == NULL)
+               return(0);
+       tblock = (char (*)[TP_BSIZE]) (((long)&req[ntrec] + pgoff) &~ pgoff);
+       req = (struct req *)tblock - ntrec;
+       return(1);
 }
 
 
 taprec(dp)
        char *dp;
 {
 }
 
 
 taprec(dp)
        char *dp;
 {
-       register i;
-
-       for (i=0; i < TP_BSIZE; i++)
-               tblock[trecno][i] = *dp++;
+       req[trecno].dblk = (daddr_t)0;
+       req[trecno].count = 1;
+       *(union u_spcl *)(*tblock++) = *(union u_spcl *)dp;     /* movc3 */
        trecno++;
        spcl.c_tapea++;
        if(trecno >= ntrec)
        trecno++;
        spcl.c_tapea++;
        if(trecno >= ntrec)
@@ -43,60 +86,76 @@ dmpblk(blkno, size)
 {
        int avail, tpblks, dblkno;
 
 {
        int avail, tpblks, dblkno;
 
-       if (size % TP_BSIZE != 0)
-               msg("bad size to dmpblk: %d\n", size);
-       avail = ntrec - trecno;
        dblkno = fsbtodb(sblock, blkno);
        dblkno = fsbtodb(sblock, blkno);
-       for (tpblks = size / TP_BSIZE; tpblks > avail; ) {
-               bread(dblkno, tblock[trecno], TP_BSIZE * avail);
+       tpblks = size / TP_BSIZE;
+       while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
+               req[trecno].dblk = dblkno;
+               req[trecno].count = avail;
                trecno += avail;
                spcl.c_tapea += avail;
                trecno += avail;
                spcl.c_tapea += avail;
-               flusht();
+               if (trecno >= ntrec)
+                       flusht();
                dblkno += avail * (TP_BSIZE / DEV_BSIZE);
                tpblks -= avail;
                dblkno += avail * (TP_BSIZE / DEV_BSIZE);
                tpblks -= avail;
-               avail = ntrec - trecno;
        }
        }
-       bread(dblkno, tblock[trecno], TP_BSIZE * tpblks);
-       trecno += tpblks;
-       spcl.c_tapea += tpblks;
-       if(trecno >= ntrec)
-               flusht();
 }
 
 int    nogripe = 0;
 
 }
 
 int    nogripe = 0;
 
+tperror() {
+       if (pipeout) {
+               msg("Tape write error on %s\n", tape);
+               msg("Cannot recover\n");
+               dumpabort();
+               /* NOTREACHED */
+       }
+       msg("Tape write error %d feet into tape %d\n", asize/120L, tapeno);
+       broadcast("TAPE ERROR!\n");
+       if (!query("Do you want to restart?"))
+               dumpabort();
+       msg("This tape will rewind.  After it is rewound,\n");
+       msg("replace the faulty tape with a new one;\n");
+       msg("this dump volume will be rewritten.\n");
+       killall();
+       nogripe = 1;
+       close_rewind();
+       Exit(X_REWRITE);
+}
+
+sigpipe()
+{
+
+       msg("Broken pipe\n");
+       dumpabort();
+}
+
+#ifdef RDUMP
+/*
+ * compatibility routine
+ */
+tflush(i)
+       int i;
+{
+
+       for (i = 0; i < ntrec; i++)
+               spclrec();
+}
+#endif RDUMP
+
 flusht()
 {
 flusht()
 {
-       register i, si;
-       daddr_t d;
+       int siz = (char *)tblock - (char *)req;
 
 
-       trecno = 0;
-       if (write(to, tblock[0], writesize) != writesize){
-               msg("Tape write error on tape %d\n", tapeno);
-               broadcast("TAPE ERROR!\n");
-               if (query("Do you want to restart?")){
-                       msg("This tape will rewind.  After it is rewound,\n");
-                       msg("replace the faulty tape with a new one;\n");
-                       msg("this dump volume will be rewritten.\n");
-                       /*
-                        *      Temporarily change the tapeno identification
-                        */
-                       tapeno--;
-                       nogripe = 1;
-                       close_rewind();
-                       nogripe = 0;
-                       tapeno++;
-                       Exit(X_REWRITE);
-               } else {
-                       dumpabort();
-                       /*NOTREACHED*/
-               }
+       if (atomic(write, slavefd[rotor], req, siz) != siz) {
+               perror("  DUMP: error writing command pipe");
+               dumpabort();
        }
        }
-
-       asize += writesize/density;
-       asize += 7;
+       if (++rotor >= SLAVES) rotor = 0;
+       tblock = (char (*)[TP_BSIZE]) &req[ntrec];
+       trecno = 0;
+       asize += tenths;
        blockswritten += ntrec;
        blockswritten += ntrec;
-       if (asize > tsize) {
+       if (!pipeout && asize > tsize) {
                close_rewind();
                otape();
        }
                close_rewind();
                otape();
        }
@@ -105,44 +164,45 @@ flusht()
 
 rewind()
 {
 
 rewind()
 {
-       int     secs;
        int f;
        int f;
-#ifdef DEBUG
-       msg("Waiting 10 seconds to rewind.\n");
-       sleep(10);
-#else
-       /*
-        *      It takes about 3 minutes, 25secs to rewind 2300' of tape
-        */
-       msg("Tape rewinding\n", secs);
+
+       if (pipeout)
+               return;
+       for (f = 0; f < SLAVES; f++)
+               close(slavefd[f]);
+       while (wait(NULL) >= 0)    ;    /* wait for any signals from slaves */
+       msg("Tape rewinding\n");
+#ifdef RDUMP
+       if (host) {
+               rmtclose();
+               while (rmtopen(tape, 0) < 0)
+                       sleep(10);
+               rmtclose();
+               return;
+       }
+#endif RDUMP
        close(to);
        while ((f = open(tape, 0)) < 0)
                sleep (10);
        close(f);
        close(to);
        while ((f = open(tape, 0)) < 0)
                sleep (10);
        close(f);
-#endif
 }
 
 close_rewind()
 {
 }
 
 close_rewind()
 {
-       close(to);
-       if (!nogripe){
-               rewind();
+       rewind();
+       if (!nogripe) {
                msg("Change Tapes: Mount tape #%d\n", tapeno+1);
                broadcast("CHANGE TAPES!\7\7\n");
        }
                msg("Change Tapes: Mount tape #%d\n", tapeno+1);
                broadcast("CHANGE TAPES!\7\7\n");
        }
-       do{
-               if (query ("Is the new tape mounted and ready to go?"))
-                       break;
-               if (query ("Do you want to abort?")){
+       while (!query("Is the new tape mounted and ready to go?"))
+               if (query("Do you want to abort?")) {
                        dumpabort();
                        /*NOTREACHED*/
                }
                        dumpabort();
                        /*NOTREACHED*/
                }
-       } while (1);
 }
 
 /*
 }
 
 /*
- *     We implement taking and restoring checkpoints on
- *     the tape level.
+ *     We implement taking and restoring checkpoints on the tape level.
  *     When each tape is opened, a new process is created by forking; this
  *     saves all of the necessary context in the parent.  The child
  *     continues the dump; the parent waits around, saving the context.
  *     When each tape is opened, a new process is created by forking; this
  *     saves all of the necessary context in the parent.  The child
  *     continues the dump; the parent waits around, saving the context.
@@ -157,13 +217,8 @@ otape()
        int     childpid;
        int     status;
        int     waitpid;
        int     childpid;
        int     status;
        int     waitpid;
-       int     sig_ign_parent();
-       int     interrupt();
+       int     (*interrupt)() = signal(SIGINT, SIG_IGN);
 
 
-       /*
-        *      Force the tape to be closed
-        */
-       close(to);
        parentpid = getpid();
 
     restore_check_point:
        parentpid = getpid();
 
     restore_check_point:
@@ -172,37 +227,32 @@ otape()
         *      All signals are inherited...
         */
        childpid = fork();
         *      All signals are inherited...
         */
        childpid = fork();
-       if (childpid < 0){
+       if (childpid < 0) {
                msg("Context save fork fails in parent %d\n", parentpid);
                Exit(X_ABORT);
        }
                msg("Context save fork fails in parent %d\n", parentpid);
                Exit(X_ABORT);
        }
-       if (childpid != 0){
+       if (childpid != 0) {
                /*
                 *      PARENT:
                 *      save the context by waiting
                 *      until the child doing all of the work returns.
                /*
                 *      PARENT:
                 *      save the context by waiting
                 *      until the child doing all of the work returns.
-                *      don't catch the interrupt 
+                *      don't catch the interrupt
                 */
                signal(SIGINT, SIG_IGN);
 #ifdef TDEBUG
                msg("Tape: %d; parent process: %d child process %d\n",
                        tapeno+1, parentpid, childpid);
 #endif TDEBUG
                 */
                signal(SIGINT, SIG_IGN);
 #ifdef TDEBUG
                msg("Tape: %d; parent process: %d child process %d\n",
                        tapeno+1, parentpid, childpid);
 #endif TDEBUG
-               for (;;){
-                       waitpid = wait(&status);
-                       if (waitpid != childpid){
-                               msg("Parent %d waiting for child %d has another child %d return\n",
-                                       parentpid, childpid, waitpid);
-                       } else
-                               break;
-               }
-               if (status & 0xFF){
+               while ((waitpid = wait(&status)) != childpid)
+                       msg("Parent %d waiting for child %d has another child %d return\n",
+                               parentpid, childpid, waitpid);
+               if (status & 0xFF) {
                        msg("Child %d returns LOB status %o\n",
                                childpid, status&0xFF);
                }
                status = (status >> 8) & 0xFF;
 #ifdef TDEBUG
                        msg("Child %d returns LOB status %o\n",
                                childpid, status&0xFF);
                }
                status = (status >> 8) & 0xFF;
 #ifdef TDEBUG
-               switch(status){
+               switch(status) {
                        case X_FINOK:
                                msg("Child %d finishes X_FINOK\n", childpid);
                                break;
                        case X_FINOK:
                                msg("Child %d finishes X_FINOK\n", childpid);
                                break;
@@ -213,11 +263,12 @@ otape()
                                msg("Child %d finishes X_REWRITE\n", childpid);
                                break;
                        default:
                                msg("Child %d finishes X_REWRITE\n", childpid);
                                break;
                        default:
-                               msg("Child %d finishes unknown %d\n", childpid,status);
+                               msg("Child %d finishes unknown %d\n",
+                                       childpid, status);
                                break;
                }
 #endif TDEBUG
                                break;
                }
 #endif TDEBUG
-               switch(status){
+               switch(status) {
                        case X_FINOK:
                                Exit(X_FINOK);
                        case X_ABORT:
                        case X_FINOK:
                                Exit(X_FINOK);
                        case X_ABORT:
@@ -234,14 +285,17 @@ otape()
                sleep(4);       /* allow time for parent's message to get out */
                msg("Child on Tape %d has parent %d, my pid = %d\n",
                        tapeno+1, parentpid, getpid());
                sleep(4);       /* allow time for parent's message to get out */
                msg("Child on Tape %d has parent %d, my pid = %d\n",
                        tapeno+1, parentpid, getpid());
-#endif
-               do{
-                       to = creat(tape, 0666);
-                       if (to < 0) {
-                               if (!query("Cannot open tape. Do you want to retry the open?"))
-                                       dumpabort();
-                       } else break;
-               } while (1);
+#endif TDEBUG
+#ifdef RDUMP
+               while ((to = (host ? rmtopen(tape, 2) :
+                       pipeout ? 1 : creat(tape, 0666))) < 0)
+#else RDUMP
+               while ((to = pipeout ? 1 : creat(tape, 0666)) < 0)
+#endif RDUMP
+                       if (!query("Cannot open tape.  Do you want to retry the open?"))
+                               dumpabort();
+
+               enslave();  /* Share open tape file descriptor with slaves */
 
                asize = 0;
                tapeno++;               /* current tape sequence */
 
                asize = 0;
                tapeno++;               /* current tape sequence */
@@ -255,18 +309,14 @@ otape()
        }
 }
 
        }
 }
 
-/*
- *     The parent still catches interrupts, but does nothing with them
- */
-sig_ign_parent()
-{
-       msg("Waiting parent receives interrupt\n");
-       signal(SIGINT, sig_ign_parent);
-}
-
 dumpabort()
 {
 dumpabort()
 {
-       msg("The ENTIRE dump is aborted.\n");
+       if (master != 0 && master != getpid())
+               kill(master, SIGTERM);  /* Signals master to call dumpabort */
+       else {
+               killall();
+               msg("The ENTIRE dump is aborted.\n");
+       }
        Exit(X_ABORT);
 }
 
        Exit(X_ABORT);
 }
 
@@ -277,3 +327,155 @@ Exit(status)
 #endif TDEBUG
        exit(status);
 }
 #endif TDEBUG
        exit(status);
 }
+
+/*
+ * could use pipe() for this if flock() worked on pipes
+ */
+lockfile(fd)
+       int fd[2];
+{
+       char tmpname[20];
+
+       strcpy(tmpname, "/tmp/dumplockXXXXXX");
+       mktemp(tmpname);
+       if ((fd[1] = creat(tmpname, 0400)) < 0) {
+               msg("Could not create lockfile ");
+               perror(tmpname);
+               dumpabort();
+       }
+       if ((fd[0] = open(tmpname, 0)) < 0) {
+               msg("Could not reopen lockfile ");
+               perror(tmpname);
+               dumpabort();
+       }
+       unlink(tmpname);
+}
+
+enslave()
+{
+       int first[2], prev[2], next[2], cmd[2];     /* file descriptors */
+       register int i, j;
+
+       master = getpid();
+       signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */
+       signal(SIGPIPE, sigpipe);
+       signal(SIGUSR1, tperror);    /* Slave sends SIGUSR1 on tape errors */
+       lockfile(first);
+       for (i = 0; i < SLAVES; i++) {
+               if (i == 0) {
+                       prev[0] = first[1];
+                       prev[1] = first[0];
+               } else {
+                       prev[0] = next[0];
+                       prev[1] = next[1];
+                       flock(prev[1], LOCK_EX);
+               }
+               if (i < SLAVES - 1) {
+                       lockfile(next);
+               } else {
+                       next[0] = first[0];
+                       next[1] = first[1];         /* Last slave loops back */
+               }
+               if (pipe(cmd) < 0 || (slavepid[i] = fork()) < 0) {
+                       msg("too many slaves, %d (recompile smaller) ", i);
+                       perror("");
+                       dumpabort();
+               }
+               slavefd[i] = cmd[1];
+               if (slavepid[i] == 0) {             /* Slave starts up here */
+                       for (j = 0; j <= i; j++)
+                               close(slavefd[j]);
+                       signal(SIGINT, SIG_IGN);    /* Master handles this */
+                       doslave(cmd[0], prev, next);
+                       Exit(X_FINOK);
+               }
+               close(cmd[0]);
+               if (i > 0) {
+                       close(prev[0]);
+                       close(prev[1]);
+               }
+       }
+       close(first[0]);
+       close(first[1]);
+       master = 0; rotor = 0;
+}
+
+killall()
+{
+       register int i;
+
+       for (i = 0; i < SLAVES; i++)
+               if (slavepid[i] > 0)
+                       kill(slavepid[i], SIGKILL);
+}
+
+/*
+ * Synchronization - each process has a lockfile, and shares file
+ * descriptors to the following process's lockfile.  When our write
+ * completes, we release our lock on the following process's lock-
+ * file, allowing the following process to lock it and proceed. We
+ * get the lock back for the next cycle by swapping descriptors.
+ */
+doslave(cmd, prev, next)
+       register int cmd, prev[2], next[2];
+{
+       register int nread, toggle = 0;
+
+       close(fi);
+       if ((fi = open(disk, 0)) < 0) {         /* Need our own seek pointer */
+               perror("  DUMP: slave couldn't reopen disk");
+               dumpabort();
+       }
+       /*
+        * Get list of blocks to dump, read the blocks into tape buffer
+        */
+       while ((nread = atomic(read, cmd, req, reqsiz)) == reqsiz) {
+               register struct req *p = req;
+               for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) {
+                       if (p->dblk) {
+                               bread(p->dblk, tblock[trecno],
+                                       p->count * TP_BSIZE);
+                       } else {
+                               if (p->count != 1 || atomic(read, cmd,
+                                   tblock[trecno], TP_BSIZE) != TP_BSIZE) {
+                                       msg("Master/slave protocol botched.\n");
+                                       dumpabort();
+                               }
+                       }
+               }
+               flock(prev[toggle], LOCK_EX);   /* Wait our turn */
+
+#ifdef RDUMP
+               if ((host ? rmtwrite(tblock[0], writesize)
+                       : write(to, tblock[0], writesize)) != writesize) {
+#else RDUMP
+               if (write(to, tblock[0], writesize) != writesize) {
+#endif RDUMP
+                       kill(master, SIGUSR1);
+                       for (;;)
+                               sigpause(0);
+               }
+               toggle ^= 1;
+               flock(next[toggle], LOCK_UN);   /* Next slave's turn */
+       }                                       /* Also jolts him awake */
+       if (nread != 0) {
+               perror("  DUMP: error reading command pipe");
+               dumpabort();
+       }
+}
+
+/*
+ * Since a read from a pipe may not return all we asked for,
+ * or a write may not write all we ask if we get a signal,
+ * loop until the count is satisfied (or error).
+ */
+atomic(func, fd, buf, count)
+       int (*func)(), fd, count;
+       char *buf;
+{
+       int got, need = count;
+
+       while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
+               buf += got;
+       return (got < 0 ? got : count - need);
+}