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