new copyright & pathname (savecore)
[unix-history] / usr / src / sbin / dump / tape.c
index 50ca9e8..c2566da 100644 (file)
@@ -1,23 +1,34 @@
+/*
+ * 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
 #ifndef lint
-static char *sccsid = "@(#)tape.c      1.9 (Berkeley) %G%";
-#endif
+static char sccsid[] = "@(#)tape.c     5.10 (Berkeley) %G%";
+#endif not lint
 
 
+#include <sys/file.h>
 #include "dump.h"
 #include "dump.h"
-#include <signal.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 "pathnames.h"
+
+char   (*tblock)[TP_BSIZE];    /* pointer to malloc()ed buffer for tape */
+int    writesize;              /* size of malloc()ed buffer for tape */
+long   lastspclrec = -1;       /* tape block number of last written header */
+int    trecno = 0;             /* next record to write in current block */
+extern int ntrec;              /* blocking factor on tape */
+extern int cartridge;
+extern int read(), write();
+#ifdef RDUMP
+extern char *host;
+#endif RDUMP
 
 /*
 
 /*
- * Streaming dump mods (Caltech) - disk block reading and tape writing
+ * 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
  * 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 pipes.  The parent process traverses the filesystem and
- * sends daddr's, inode records, etc, through pipes to each slave.
- * Speed from Eagle to TU77 on VAX/780 is about 140 Kbytes/second.
- * #ifdef RDUMP version is CPU-limited to about 40 Kbytes/second.
+ * 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;
  */
 struct req {                   /* instruction packets sent to slaves */
        daddr_t dblk;
@@ -25,45 +36,50 @@ struct req {                        /* instruction packets sent to slaves */
 } *req;
 int reqsiz;
 
 } *req;
 int reqsiz;
 
-#define SLAVES 3               /* 2 slaves read disk while 3rd writes tape */
-#define LAG 2                  /* Write behind by LAG tape blocks (rdump) */
-int slavefd[SLAVES];           /* Pipes from master to each slave */
-int rotor;                     /* Current slave number */
-int master;                    /* Pid of master, for sending error signals */
-int trace = 0;                 /* Protocol trace; easily patchable with adb */
-#define  tmsg  if (trace) msg
-
-#ifdef RDUMP
-extern int rmtape;
-#endif
+#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 */
 
 
-/*
- * Allocate tape buffer contiguous with the array of instruction packets,
- * so they can be written with a single write call in flusht().
- */
 alloctape()
 {
 alloctape()
 {
+       int pgoff = getpagesize() - 1;
 
        writesize = ntrec * TP_BSIZE;
        reqsiz = ntrec * sizeof(struct req);
 
        writesize = ntrec * TP_BSIZE;
        reqsiz = ntrec * sizeof(struct req);
-       req = (struct req *)malloc(reqsiz+writesize);   /* array of packets */
-       tblock = (char (*)[TP_BSIZE]) &req[ntrec];      /* Tape buffer */
-       return (req != NULL);
+       /*
+        * 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);
 }
 
 }
 
-/*
- * Send special record to be put on tape
- */
+
 taprec(dp)
        char *dp;
 {
 taprec(dp)
        char *dp;
 {
-
-       tmsg("taprec %d\n", trecno);
        req[trecno].dblk = (daddr_t)0;
        req[trecno].count = 1;
        req[trecno].dblk = (daddr_t)0;
        req[trecno].count = 1;
-       *(union u_spcl *)(*tblock++) = *(union u_spcl *)dp;
+       *(union u_spcl *)(*tblock++) = *(union u_spcl *)dp;     /* movc3 */
+       lastspclrec = spcl.c_tapea;
+       trecno++;
        spcl.c_tapea++;
        spcl.c_tapea++;
-       if (++trecno >= ntrec)
+       if(trecno >= ntrec)
                flusht();
 }
 
                flusht();
 }
 
@@ -71,22 +87,18 @@ dmpblk(blkno, size)
        daddr_t blkno;
        int size;
 {
        daddr_t blkno;
        int size;
 {
-       int tpblks, dblkno;
-       register int avail;
+       int avail, tpblks, dblkno;
 
 
-       if (size % TP_BSIZE != 0)
-               msg("bad size to dmpblk: %d\n", size);
        dblkno = fsbtodb(sblock, blkno);
        tpblks = size / TP_BSIZE;
        while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
        dblkno = fsbtodb(sblock, blkno);
        tpblks = size / TP_BSIZE;
        while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
-               tmsg("dmpblk %d\n", avail);
                req[trecno].dblk = dblkno;
                req[trecno].count = avail;
                trecno += avail;
                spcl.c_tapea += avail;
                if (trecno >= ntrec)
                        flusht();
                req[trecno].dblk = dblkno;
                req[trecno].count = avail;
                trecno += avail;
                spcl.c_tapea += avail;
                if (trecno >= ntrec)
                        flusht();
-               dblkno += avail * (TP_BSIZE / DEV_BSIZE);
+               dblkno += avail * (TP_BSIZE / dev_bsize);
                tpblks -= avail;
        }
 }
                tpblks -= avail;
        }
 }
@@ -100,30 +112,33 @@ tperror() {
                dumpabort();
                /* NOTREACHED */
        }
                dumpabort();
                /* NOTREACHED */
        }
-       msg("Tape write error on tape %d\n", tapeno);
+       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");
        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);
 }
 
        nogripe = 1;
        close_rewind();
        Exit(X_REWRITE);
 }
 
-senderr()
+sigpipe()
 {
 
 {
 
-       perror("dump: pipe error in command to slave");
+       msg("Broken pipe\n");
        dumpabort();
 }
 
 #ifdef RDUMP
        dumpabort();
 }
 
 #ifdef RDUMP
-tflush(cnt)
-       int cnt;
-{
+/*
+ * compatibility routine
+ */
+tflush(i)
        int i;
        int i;
+{
 
        for (i = 0; i < ntrec; i++)
                spclrec();
 
        for (i = 0; i < ntrec; i++)
                spclrec();
@@ -132,18 +147,16 @@ tflush(cnt)
 
 flusht()
 {
 
 flusht()
 {
-       int sig, siz = (char *)tblock - (char *)req;
+       int siz = (char *)tblock - (char *)req;
 
 
-       tmsg("flusht %d\n", siz);
-       sig = sigblock(1<<SIGINT-1 | 1<<SIGIOT-1);  /* Don't interrupt write */
-       if (write(slavefd[rotor], req, siz) != siz)
-               senderr();
-       sigsetmask(sig);
+       if (atomic(write, slavefd[rotor], req, siz) != siz) {
+               perror("  DUMP: error writing command pipe");
+               dumpabort();
+       }
        if (++rotor >= SLAVES) rotor = 0;
        tblock = (char (*)[TP_BSIZE]) &req[ntrec];
        trecno = 0;
        if (++rotor >= SLAVES) rotor = 0;
        tblock = (char (*)[TP_BSIZE]) &req[ntrec];
        trecno = 0;
-       asize += writesize/density;
-       asize += 7;                     /* inter-record gap (why fixed?) */
+       asize += tenths;
        blockswritten += ntrec;
        if (!pipeout && asize > tsize) {
                close_rewind();
        blockswritten += ntrec;
        if (!pipeout && asize > tsize) {
                close_rewind();
@@ -154,7 +167,7 @@ flusht()
 
 rewind()
 {
 
 rewind()
 {
-       register int f;
+       int f;
 
        if (pipeout)
                return;
 
        if (pipeout)
                return;
@@ -163,16 +176,18 @@ rewind()
        while (wait(NULL) >= 0)    ;    /* wait for any signals from slaves */
        msg("Tape rewinding\n");
 #ifdef RDUMP
        while (wait(NULL) >= 0)    ;    /* wait for any signals from slaves */
        msg("Tape rewinding\n");
 #ifdef RDUMP
-       rmtclose();
-       while (rmtopen(tape, 0) < 0)
-               sleep(10);
-       rmtclose();
-#else
+       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()
@@ -183,8 +198,10 @@ close_rewind()
                broadcast("CHANGE TAPES!\7\7\n");
        }
        while (!query("Is the new tape mounted and ready to go?"))
                broadcast("CHANGE TAPES!\7\7\n");
        }
        while (!query("Is the new tape mounted and ready to go?"))
-               if (query("Do you want to abort?"))
+               if (query("Do you want to abort?")) {
                        dumpabort();
                        dumpabort();
+                       /*NOTREACHED*/
+               }
 }
 
 /*
 }
 
 /*
@@ -203,12 +220,14 @@ otape()
        int     childpid;
        int     status;
        int     waitpid;
        int     childpid;
        int     status;
        int     waitpid;
-       int     interrupt();
+       sig_t   interrupt;
+       int     blks, i;
 
 
+       interrupt = signal(SIGINT, SIG_IGN);
        parentpid = getpid();
 
     restore_check_point:
        parentpid = getpid();
 
     restore_check_point:
-       signal(SIGINT, interrupt);
+       (void)signal(SIGINT, interrupt);
        /*
         *      All signals are inherited...
         */
        /*
         *      All signals are inherited...
         */
@@ -250,7 +269,7 @@ otape()
                                break;
                        default:
                                msg("Child %d finishes unknown %d\n",
                                break;
                        default:
                                msg("Child %d finishes unknown %d\n",
-                                   childpid, status);
+                                       childpid, status);
                                break;
                }
 #endif TDEBUG
                                break;
                }
 #endif TDEBUG
@@ -271,23 +290,35 @@ 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
+#endif TDEBUG
 #ifdef RDUMP
 #ifdef RDUMP
-               while ((to = rmtopen(tape, 2)) < 0)
-#else
+               while ((to = (host ? rmtopen(tape, 2) :
+                       pipeout ? 1 : creat(tape, 0666))) < 0)
+#else RDUMP
                while ((to = pipeout ? 1 : creat(tape, 0666)) < 0)
                while ((to = pipeout ? 1 : creat(tape, 0666)) < 0)
-#endif
-                       if (!query("Cannot open tape.  Do you want to retry the open?"))
+#endif RDUMP
+                   {
+                       msg("Cannot open tape \"%s\".\n", tape);
+                       if (!query("Do you want to retry the open?"))
                                dumpabort();
                                dumpabort();
+               }
 
                enslave();  /* Share open tape file descriptor with slaves */
 
                asize = 0;
                tapeno++;               /* current tape sequence */
                newtape++;              /* new tape signal */
 
                enslave();  /* Share open tape file descriptor with slaves */
 
                asize = 0;
                tapeno++;               /* current tape sequence */
                newtape++;              /* new tape signal */
+               blks = 0;
+               if (spcl.c_type != TS_END)
+                       for (i = 0; i < spcl.c_count; i++)
+                               if (spcl.c_addr[i] != 0)
+                                       blks++;
+               spcl.c_count = blks + 1 - spcl.c_tapea + lastspclrec;
                spcl.c_volume++;
                spcl.c_type = TS_TAPE;
                spcl.c_volume++;
                spcl.c_type = TS_TAPE;
+               spcl.c_flags |= DR_NEWHEADER;
                spclrec();
                spclrec();
+               spcl.c_flags &=~ DR_NEWHEADER;
                if (tapeno > 1)
                        msg("Tape %d begins with blocks from ino %d\n",
                                tapeno, ino);
                if (tapeno > 1)
                        msg("Tape %d begins with blocks from ino %d\n",
                                tapeno, ino);
@@ -297,8 +328,11 @@ otape()
 dumpabort()
 {
        if (master != 0 && master != getpid())
 dumpabort()
 {
        if (master != 0 && master != getpid())
-               kill(master, SIGIOT);
-       msg("The ENTIRE dump is aborted.\n");
+               kill(master, SIGTERM);  /* Signals master to call dumpabort */
+       else {
+               killall();
+               msg("The ENTIRE dump is aborted.\n");
+       }
        Exit(X_ABORT);
 }
 
        Exit(X_ABORT);
 }
 
@@ -310,116 +344,154 @@ Exit(status)
        exit(status);
 }
 
        exit(status);
 }
 
-#define OK 020
-char tok = OK;
+/*
+ * could use pipe() for this if flock() worked on pipes
+ */
+lockfile(fd)
+       int fd[2];
+{
+       char tmpname[20];
+
+       strcpy(tmpname, _PATH_LOCK);
+       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()
 {
 
 enslave()
 {
-       int prev[2], next[2], cmd[2];   /* file descriptors for pipes */
-       int i, j, slavepid;
+       int first[2], prev[2], next[2], cmd[2];     /* file descriptors */
+       register int i, j;
 
        master = getpid();
 
        master = getpid();
-       signal(SIGPIPE, dumpabort);
-       signal(SIGIOT, tperror); /* SIGIOT asks for restart from checkpoint */
-       pipe(prev);
-       for (i = rotor = 0; i < SLAVES; ++i) {
-               if ((i < SLAVES - 1 && pipe(next) < 0) || pipe(cmd) < 0
-                               || (slavepid = fork()) < 0) {
-                       perror("  DUMP: too many slaves");
+       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();
                }
                        dumpabort();
                }
-               if (i >= SLAVES - 1)
-                       next[1] = prev[1];          /* Last slave loops back */
                slavefd[i] = cmd[1];
                slavefd[i] = cmd[1];
-               if (slavepid == 0) {                /* Slave starts up here */
+               if (slavepid[i] == 0) {             /* Slave starts up here */
                        for (j = 0; j <= i; j++)
                                close(slavefd[j]);
                        for (j = 0; j <= i; j++)
                                close(slavefd[j]);
-                       if (i < SLAVES - 1) {
-                               close(prev[1]);
-                               close(next[0]);
-                       } else {                    /* Insert initial token */
-                               if (write(next[1], &tok, 1) != 1)
-                                       ringerr();
-                       }
-                       doslave(i, cmd[0], prev[0], next[1]);
-                       close(next[1]);
-                       j = read(prev[0], &tok, 1);   /* Eat the final token */
-#ifdef RDUMP                               /* Read remaining acknowledges */
-                       for (; j > 0 && (tok &~ OK) > 0; tok--) {
-                               if (rmtwrite2() != writesize && (tok & OK)) {
-                                       kill(master, SIGIOT);
-                                       tok &= ~OK;
-                               }
-                       }
-#endif
+                       signal(SIGINT, SIG_IGN);    /* Master handles this */
+                       doslave(cmd[0], prev, next);
                        Exit(X_FINOK);
                }
                close(cmd[0]);
                        Exit(X_FINOK);
                }
                close(cmd[0]);
-               close(next[1]);
-               close(prev[0]);
-               prev[0] = next[0];
+               if (i > 0) {
+                       close(prev[0]);
+                       close(prev[1]);
+               }
        }
        }
-       master = 0;
+       close(first[0]);
+       close(first[1]);
+       master = 0; rotor = 0;
 }
 
 }
 
-/*
- * Somebody must have died, should never happen
- */
-ringerr()
+killall()
 {
 {
-       perror("  DUMP: token passing error");
-       kill(master, SIGPIPE);
-       Exit(X_ABORT);
+       register int i;
+
+       for (i = 0; i < SLAVES; i++)
+               if (slavepid[i] > 0)
+                       kill(slavepid[i], SIGKILL);
 }
 
 }
 
-doslave(num, cmd, prev, next)
-       int num, cmd, prev, next;
+/*
+ * 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];
 {
 {
-       tmsg("slave %d\n", num);
-       signal(SIGINT, SIG_IGN);                /* Master handles it */
-       signal(SIGTERM, SIG_IGN);
-       signal(SIGPIPE, ringerr);
+       register int nread, toggle = 0;
+
        close(fi);
        close(fi);
-       if ((fi = open(disk, 0)) < 0) {         /* Need our own seek pointer */
-               perror("  DUMP: can't reopen disk");
-               kill(master, SIGPIPE);
-               Exit(X_ABORT);
+       if ((fi = open(disk, 0)) < 0) {         /* Need our own seek pointer */
+               perror("  DUMP: slave couldn't reopen disk");
+               dumpabort();
        }
        }
-       while (read(cmd, req, reqsiz) == reqsiz) {
+       /*
+        * 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) {
                register struct req *p = req;
                for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) {
                        if (p->dblk) {
-                               tmsg("%d READS %d\n", num, p->count);
                                bread(p->dblk, tblock[trecno],
                                bread(p->dblk, tblock[trecno],
-                                   p->count * TP_BSIZE);
+                                       p->count * TP_BSIZE);
                        } else {
                        } else {
-                               tmsg("%d PIPEIN %d\n", num, p->count);
-                               if (p->count != 1)
-                                       ringerr();
-                               if (read(cmd, tblock[trecno], TP_BSIZE) != TP_BSIZE)
-                                       senderr();
+                               if (p->count != 1 || atomic(read, cmd,
+                                   tblock[trecno], TP_BSIZE) != TP_BSIZE) {
+                                       msg("Master/slave protocol botched.\n");
+                                       dumpabort();
+                               }
                        }
                }
                        }
                }
-               if (read(prev, &tok, 1) != 1)
-                       ringerr();      /* Wait your turn */
-               tmsg("%d WRITE\n", num);
+               flock(prev[toggle], LOCK_EX);   /* Wait our turn */
+
 #ifdef RDUMP
 #ifdef RDUMP
-               if (tok & OK) {
-                       rmtwrite0(writesize);
-                       rmtwrite1(tblock[0], writesize);
-                       tok++;          /* Number of writes in progress */
-               }
-               if (tok > (LAG|OK) && (--tok, rmtwrite2() != writesize)) {
-#else
-               if ((tok & OK) &&
-                   write(to, tblock[0], writesize) != writesize) {
-                       perror(tape);
-#endif
-                       kill(master, SIGIOT);   /* restart from checkpoint */
-                       tok &= ~OK;
+               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);
                }
                }
-               if (write(next, &tok, 1) != 1)
-                       ringerr(); /* Next slave's turn */
+               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();
        }
        }
-       tmsg("%d CLOSE\n", num);
+}
+
+/*
+ * 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);
 }
 }