1 /* 2 * Copyright (c) 1980 Regents of the University of California. 3 * All rights reserved. The Berkeley software License Agreement 4 * specifies the terms and conditions for redistribution. 5 */ 6 7 #ifndef lint 8 static char sccsid[] = "@(#)tape.c 5.15 (Berkeley) 02/28/91"; 9 #endif /* not lint */ 10 11 #include <sys/param.h> 12 #include <sys/wait.h> 13 #include <ufs/dir.h> 14 #include <ufs/dinode.h> 15 #include <ufs/fs.h> 16 #include <signal.h> 17 #include <fcntl.h> 18 #include <protocols/dumprestore.h> 19 #include <errno.h> 20 #ifdef __STDC__ 21 #include <unistd.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #endif 25 #include "dump.h" 26 #include "pathnames.h" 27 28 char (*tblock)[TP_BSIZE]; /* pointer to malloc()ed buffer for tape */ 29 int writesize; /* size of malloc()ed buffer for tape */ 30 long lastspclrec = -1; /* tape block number of last written header */ 31 int trecno = 0; /* next record to write in current block */ 32 extern long blocksperfile; /* number of blocks per output file */ 33 extern int ntrec; /* blocking factor on tape */ 34 extern int cartridge; 35 #ifdef RDUMP 36 extern char *host; 37 int rmtopen(), rmtwrite(); 38 void rmtclose(); 39 #endif RDUMP 40 41 int atomic(); 42 void doslave(), enslave(), flushtape(), killall(); 43 44 /* 45 * Concurrent dump mods (Caltech) - disk block reading and tape writing 46 * are exported to several slave processes. While one slave writes the 47 * tape, the others read disk blocks; they pass control of the tape in 48 * a ring via flock(). The parent process traverses the filesystem and 49 * sends writeheader()'s and lists of daddr's to the slaves via pipes. 50 */ 51 struct req { /* instruction packets sent to slaves */ 52 daddr_t dblk; 53 int count; 54 } *req; 55 int reqsiz; 56 57 #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ 58 int slavefd[SLAVES]; /* pipes from master to each slave */ 59 int slavepid[SLAVES]; /* used by killall() */ 60 int rotor; /* next slave to be instructed */ 61 int master; /* pid of master, for sending error signals */ 62 int tenths; /* length of tape used per block written */ 63 64 int 65 alloctape() 66 { 67 int pgoff = getpagesize() - 1; 68 69 writesize = ntrec * TP_BSIZE; 70 reqsiz = ntrec * sizeof(struct req); 71 /* 72 * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode 73 * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require 74 * repositioning after stopping, i.e, streaming mode, where the gap is 75 * variable, 0.30" to 0.45". The gap is maximal when the tape stops. 76 */ 77 tenths = writesize/density + (cartridge ? 16 : density == 625 ? 5 : 8); 78 /* 79 * Allocate tape buffer contiguous with the array of instruction 80 * packets, so flushtape() can write them together with one write(). 81 * Align tape buffer on page boundary to speed up tape write(). 82 */ 83 req = (struct req *)malloc(reqsiz + writesize + pgoff); 84 if (req == NULL) 85 return(0); 86 tblock = (char (*)[TP_BSIZE]) (((long)&req[ntrec] + pgoff) &~ pgoff); 87 req = (struct req *)tblock - ntrec; 88 return(1); 89 } 90 91 92 void 93 writerec(dp) 94 char *dp; 95 { 96 req[trecno].dblk = (daddr_t)0; 97 req[trecno].count = 1; 98 *(union u_spcl *)(*tblock++) = *(union u_spcl *)dp; /* movc3 */ 99 lastspclrec = spcl.c_tapea; 100 trecno++; 101 spcl.c_tapea++; 102 if (trecno >= ntrec) 103 flushtape(); 104 } 105 106 void 107 dumpblock(blkno, size) 108 daddr_t blkno; 109 int size; 110 { 111 int avail, tpblks, dblkno; 112 113 dblkno = fsbtodb(sblock, blkno); 114 tpblks = size >> tp_bshift; 115 while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { 116 req[trecno].dblk = dblkno; 117 req[trecno].count = avail; 118 trecno += avail; 119 spcl.c_tapea += avail; 120 if (trecno >= ntrec) 121 flushtape(); 122 dblkno += avail << (tp_bshift - dev_bshift); 123 tpblks -= avail; 124 } 125 } 126 127 int nogripe = 0; 128 129 void 130 tperror() 131 { 132 if (pipeout) { 133 msg("write error on %s\n", tape); 134 quit("Cannot recover\n"); 135 /* NOTREACHED */ 136 } 137 msg("write error %d blocks into volume %d\n", blockswritten, tapeno); 138 broadcast("DUMP WRITE ERROR!\n"); 139 if (!query("Do you want to restart?")) 140 dumpabort(); 141 msg("Closing this volume. Prepare to restart with new media;\n"); 142 msg("this dump volume will be rewritten.\n"); 143 killall(); 144 nogripe = 1; 145 close_rewind(); 146 Exit(X_REWRITE); 147 } 148 149 void 150 sigpipe() 151 { 152 153 quit("Broken pipe\n"); 154 } 155 156 void 157 flushtape() 158 { 159 #ifndef __STDC__ 160 int write(); 161 #endif 162 163 int siz = (char *)tblock - (char *)req; 164 165 if (atomic(write, slavefd[rotor], req, siz) != siz) 166 quit("error writing command pipe: %s\n", strerror(errno)); 167 if (++rotor >= SLAVES) 168 rotor = 0; 169 tblock = (char (*)[TP_BSIZE]) &req[ntrec]; 170 trecno = 0; 171 asize += tenths; 172 blockswritten += ntrec; 173 if (!pipeout && (blocksperfile ? 174 (blockswritten >= blocksperfile) : (asize > tsize))) { 175 close_rewind(); 176 startnewtape(); 177 } 178 timeest(); 179 } 180 181 void 182 trewind() 183 { 184 int f; 185 186 if (pipeout) 187 return; 188 for (f = 0; f < SLAVES; f++) 189 close(slavefd[f]); 190 while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */ 191 /* void */; 192 msg("Tape rewinding\n"); 193 #ifdef RDUMP 194 if (host) { 195 rmtclose(); 196 while (rmtopen(tape, 0) < 0) 197 sleep(10); 198 rmtclose(); 199 return; 200 } 201 #endif RDUMP 202 close(tapefd); 203 while ((f = open(tape, 0)) < 0) 204 sleep (10); 205 close(f); 206 } 207 208 void 209 close_rewind() 210 { 211 trewind(); 212 if (!nogripe) { 213 msg("Change Volumes: Mount volume #%d\n", tapeno+1); 214 broadcast("CHANGE DUMP VOLUMES!\7\7\n"); 215 } 216 while (!query("Is the new volume mounted and ready to go?")) 217 if (query("Do you want to abort?")) { 218 dumpabort(); 219 /*NOTREACHED*/ 220 } 221 } 222 223 /* 224 * We implement taking and restoring checkpoints on the tape level. 225 * When each tape is opened, a new process is created by forking; this 226 * saves all of the necessary context in the parent. The child 227 * continues the dump; the parent waits around, saving the context. 228 * If the child returns X_REWRITE, then it had problems writing that tape; 229 * this causes the parent to fork again, duplicating the context, and 230 * everything continues as if nothing had happened. 231 */ 232 233 void 234 startnewtape() 235 { 236 int parentpid; 237 int childpid; 238 int status; 239 int waitpid; 240 sig_t interrupt; 241 int blks, i; 242 243 interrupt = signal(SIGINT, SIG_IGN); 244 parentpid = getpid(); 245 246 restore_check_point: 247 (void)signal(SIGINT, interrupt); 248 /* 249 * All signals are inherited... 250 */ 251 childpid = fork(); 252 if (childpid < 0) { 253 msg("Context save fork fails in parent %d\n", parentpid); 254 Exit(X_ABORT); 255 } 256 if (childpid != 0) { 257 /* 258 * PARENT: 259 * save the context by waiting 260 * until the child doing all of the work returns. 261 * don't catch the interrupt 262 */ 263 signal(SIGINT, SIG_IGN); 264 #ifdef TDEBUG 265 msg("Tape: %d; parent process: %d child process %d\n", 266 tapeno+1, parentpid, childpid); 267 #endif TDEBUG 268 while ((waitpid = wait(&status)) != childpid) 269 msg("Parent %d waiting for child %d has another child %d return\n", 270 parentpid, childpid, waitpid); 271 if (status & 0xFF) { 272 msg("Child %d returns LOB status %o\n", 273 childpid, status&0xFF); 274 } 275 status = (status >> 8) & 0xFF; 276 #ifdef TDEBUG 277 switch(status) { 278 case X_FINOK: 279 msg("Child %d finishes X_FINOK\n", childpid); 280 break; 281 case X_ABORT: 282 msg("Child %d finishes X_ABORT\n", childpid); 283 break; 284 case X_REWRITE: 285 msg("Child %d finishes X_REWRITE\n", childpid); 286 break; 287 default: 288 msg("Child %d finishes unknown %d\n", 289 childpid, status); 290 break; 291 } 292 #endif TDEBUG 293 switch(status) { 294 case X_FINOK: 295 Exit(X_FINOK); 296 case X_ABORT: 297 Exit(X_ABORT); 298 case X_REWRITE: 299 goto restore_check_point; 300 default: 301 msg("Bad return code from dump: %d\n", status); 302 Exit(X_ABORT); 303 } 304 /*NOTREACHED*/ 305 } else { /* we are the child; just continue */ 306 #ifdef TDEBUG 307 sleep(4); /* allow time for parent's message to get out */ 308 msg("Child on Tape %d has parent %d, my pid = %d\n", 309 tapeno+1, parentpid, getpid()); 310 #endif TDEBUG 311 #ifdef RDUMP 312 while ((tapefd = (host ? rmtopen(tape, 2) : 313 pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 314 #else RDUMP 315 while ((tapefd = 316 pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666)) < 0) 317 #endif RDUMP 318 { 319 msg("Cannot open output \"%s\".\n", tape); 320 if (!query("Do you want to retry the open?")) 321 dumpabort(); 322 } 323 324 enslave(); /* Share open tape file descriptor with slaves */ 325 326 asize = 0; 327 tapeno++; /* current tape sequence */ 328 newtape++; /* new tape signal */ 329 blks = 0; 330 if (spcl.c_type != TS_END) 331 for (i = 0; i < spcl.c_count; i++) 332 if (spcl.c_addr[i] != 0) 333 blks++; 334 spcl.c_count = blks + 1 - spcl.c_tapea + lastspclrec; 335 spcl.c_volume++; 336 spcl.c_type = TS_TAPE; 337 spcl.c_flags |= DR_NEWHEADER; 338 writeheader(curino); 339 spcl.c_flags &=~ DR_NEWHEADER; 340 if (tapeno > 1) 341 msg("Tape %d begins with blocks from inode %d\n", 342 tapeno, curino); 343 } 344 } 345 346 void 347 dumpabort() 348 { 349 if (master != 0 && master != getpid()) 350 kill(master, SIGTERM); /* Signals master to call dumpabort */ 351 else { 352 killall(); 353 msg("The ENTIRE dump is aborted.\n"); 354 } 355 Exit(X_ABORT); 356 } 357 358 void 359 Exit(status) 360 int status; 361 { 362 #ifdef TDEBUG 363 msg("pid = %d exits with status %d\n", getpid(), status); 364 #endif TDEBUG 365 exit(status); 366 } 367 368 /* 369 * could use pipe() for this if flock() worked on pipes 370 */ 371 void 372 lockfile(fd) 373 int fd[2]; 374 { 375 char tmpname[20]; 376 377 strcpy(tmpname, _PATH_LOCK); 378 mktemp(tmpname); 379 if ((fd[1] = creat(tmpname, 0400)) < 0) 380 quit("cannot create lockfile %s: %s\n", 381 tmpname, strerror(errno)); 382 if ((fd[0] = open(tmpname, 0)) < 0) 383 quit("cannot reopen lockfile %s: %s\n", 384 tmpname, strerror(errno)); 385 (void) unlink(tmpname); 386 } 387 388 void 389 enslave() 390 { 391 int first[2], prev[2], next[2], cmd[2]; /* file descriptors */ 392 register int i, j; 393 394 master = getpid(); 395 signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ 396 signal(SIGPIPE, sigpipe); 397 signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ 398 lockfile(first); 399 for (i = 0; i < SLAVES; i++) { 400 if (i == 0) { 401 prev[0] = first[1]; 402 prev[1] = first[0]; 403 } else { 404 prev[0] = next[0]; 405 prev[1] = next[1]; 406 flock(prev[1], LOCK_EX); 407 } 408 if (i < SLAVES - 1) { 409 lockfile(next); 410 } else { 411 next[0] = first[0]; 412 next[1] = first[1]; /* Last slave loops back */ 413 } 414 if (pipe(cmd) < 0 || (slavepid[i] = fork()) < 0) 415 quit("too many slaves, %d (recompile smaller): %s\n", 416 i, strerror(errno)); 417 slavefd[i] = cmd[1]; 418 if (slavepid[i] == 0) { /* Slave starts up here */ 419 for (j = 0; j <= i; j++) 420 close(slavefd[j]); 421 signal(SIGINT, SIG_IGN); /* Master handles this */ 422 doslave(cmd[0], prev, next); 423 Exit(X_FINOK); 424 } 425 close(cmd[0]); 426 if (i > 0) { 427 close(prev[0]); 428 close(prev[1]); 429 } 430 } 431 close(first[0]); 432 close(first[1]); 433 master = 0; rotor = 0; 434 } 435 436 void 437 killall() 438 { 439 register int i; 440 441 for (i = 0; i < SLAVES; i++) 442 if (slavepid[i] > 0) 443 kill(slavepid[i], SIGKILL); 444 } 445 446 /* 447 * Synchronization - each process has a lockfile, and shares file 448 * descriptors to the following process's lockfile. When our write 449 * completes, we release our lock on the following process's lock- 450 * file, allowing the following process to lock it and proceed. We 451 * get the lock back for the next cycle by swapping descriptors. 452 */ 453 void 454 doslave(cmd, prev, next) 455 register int cmd, prev[2], next[2]; 456 { 457 register int nread, toggle = 0; 458 #ifndef __STDC__ 459 int read(); 460 #endif 461 462 /* 463 * Need our own seek pointer. 464 */ 465 close(diskfd); 466 if ((diskfd = open(disk, O_RDONLY)) < 0) 467 quit("slave couldn't reopen disk: %s\n", strerror(errno)); 468 /* 469 * Get list of blocks to dump, read the blocks into tape buffer 470 */ 471 while ((nread = atomic(read, cmd, req, reqsiz)) == reqsiz) { 472 register struct req *p = req; 473 for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) { 474 if (p->dblk) { 475 bread(p->dblk, tblock[trecno], 476 p->count * TP_BSIZE); 477 } else { 478 if (p->count != 1 || atomic(read, cmd, 479 tblock[trecno], TP_BSIZE) != TP_BSIZE) 480 quit("master/slave protocol botched.\n"); 481 } 482 } 483 flock(prev[toggle], LOCK_EX); /* Wait our turn */ 484 485 #ifdef RDUMP 486 if ((host ? rmtwrite(tblock[0], writesize) 487 : write(tapefd, tblock[0], writesize)) != writesize) { 488 #else RDUMP 489 if (write(tapefd, tblock[0], writesize) != writesize) { 490 #endif RDUMP 491 kill(master, SIGUSR1); 492 for (;;) 493 sigpause(0); 494 } 495 toggle ^= 1; 496 flock(next[toggle], LOCK_UN); /* Next slave's turn */ 497 } /* Also jolts him awake */ 498 if (nread != 0) 499 quit("error reading command pipe: %s\n", strerror(errno)); 500 } 501 502 /* 503 * Since a read from a pipe may not return all we asked for, 504 * or a write may not write all we ask if we get a signal, 505 * loop until the count is satisfied (or error). 506 */ 507 int 508 atomic(func, fd, buf, count) 509 int (*func)(), fd, count; 510 char *buf; 511 { 512 int got, need = count; 513 514 while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) 515 buf += got; 516 return (got < 0 ? got : count - need); 517 } 518