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