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.1 (Berkeley) 06/05/85"; 9 #endif not lint 10 11 #include "dump.h" 12 #include <signal.h> 13 14 char (*tblock)[TP_BSIZE]; /* Pointer to malloc()ed buffer for tape */ 15 int writesize; /* Size of malloc()ed buffer for tape */ 16 int trecno = 0; 17 extern int ntrec; /* blocking factor on tape */ 18 19 /* 20 * Streaming dump mods (Caltech) - disk block reading and tape writing 21 * are exported to several slave processes. While one slave writes the 22 * tape, the others read disk blocks; they pass control of the tape in 23 * a ring via pipes. The parent process traverses the filesystem and 24 * sends daddr's, inode records, etc, through pipes to each slave. 25 * Speed from Eagle to TU77 on VAX/780 is about 140 Kbytes/second. 26 * #ifdef RDUMP version is CPU-limited to about 40 Kbytes/second. 27 */ 28 struct req { /* instruction packets sent to slaves */ 29 daddr_t dblk; 30 int count; 31 } *req; 32 int reqsiz; 33 34 #define SLAVES 3 /* 2 slaves read disk while 3rd writes tape */ 35 #define LAG 2 /* Write behind by LAG tape blocks (rdump) */ 36 int slavefd[SLAVES]; /* Pipes from master to each slave */ 37 int rotor; /* Current slave number */ 38 int master; /* Pid of master, for sending error signals */ 39 int trace = 0; /* Protocol trace; easily patchable with adb */ 40 #define tmsg if (trace) msg 41 42 #ifdef RDUMP 43 extern int rmtape; 44 #endif 45 46 /* 47 * Allocate tape buffer contiguous with the array of instruction packets, 48 * so they can be written with a single write call in flusht(). 49 */ 50 alloctape() 51 { 52 53 writesize = ntrec * TP_BSIZE; 54 reqsiz = ntrec * sizeof(struct req); 55 req = (struct req *)malloc(reqsiz+writesize); /* array of packets */ 56 tblock = (char (*)[TP_BSIZE]) &req[ntrec]; /* Tape buffer */ 57 return (req != NULL); 58 } 59 60 /* 61 * Send special record to be put on tape 62 */ 63 taprec(dp) 64 char *dp; 65 { 66 67 tmsg("taprec %d\n", trecno); 68 req[trecno].dblk = (daddr_t)0; 69 req[trecno].count = 1; 70 *(union u_spcl *)(*tblock++) = *(union u_spcl *)dp; 71 spcl.c_tapea++; 72 if (++trecno >= ntrec) 73 flusht(); 74 } 75 76 dmpblk(blkno, size) 77 daddr_t blkno; 78 int size; 79 { 80 int tpblks, dblkno; 81 register int avail; 82 83 if (size % TP_BSIZE != 0) 84 msg("bad size to dmpblk: %d\n", size); 85 dblkno = fsbtodb(sblock, blkno); 86 tpblks = size / TP_BSIZE; 87 while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { 88 tmsg("dmpblk %d\n", avail); 89 req[trecno].dblk = dblkno; 90 req[trecno].count = avail; 91 trecno += avail; 92 spcl.c_tapea += avail; 93 if (trecno >= ntrec) 94 flusht(); 95 dblkno += avail * (TP_BSIZE / DEV_BSIZE); 96 tpblks -= avail; 97 } 98 } 99 100 int nogripe = 0; 101 102 tperror() { 103 if (pipeout) { 104 msg("Tape write error on %s\n", tape); 105 msg("Cannot recover\n"); 106 dumpabort(); 107 /* NOTREACHED */ 108 } 109 msg("Tape write error on tape %d\n", tapeno); 110 broadcast("TAPE ERROR!\n"); 111 if (!query("Do you want to restart?")) 112 dumpabort(); 113 msg("This tape will rewind. After it is rewound,\n"); 114 msg("replace the faulty tape with a new one;\n"); 115 msg("this dump volume will be rewritten.\n"); 116 nogripe = 1; 117 close_rewind(); 118 Exit(X_REWRITE); 119 } 120 121 senderr() 122 { 123 124 perror(" DUMP: pipe error in command to slave"); 125 dumpabort(); 126 } 127 128 #ifdef RDUMP 129 tflush(cnt) 130 int cnt; 131 { 132 int i; 133 134 for (i = 0; i < ntrec; i++) 135 spclrec(); 136 } 137 #endif RDUMP 138 139 flusht() 140 { 141 int sig, siz = (char *)tblock - (char *)req; 142 143 tmsg("flusht %d\n", siz); 144 sig = sigblock(1<<SIGINT-1 | 1<<SIGIOT-1); /* Don't interrupt write */ 145 if (write(slavefd[rotor], req, siz) != siz) 146 senderr(); 147 sigsetmask(sig); 148 if (++rotor >= SLAVES) rotor = 0; 149 tblock = (char (*)[TP_BSIZE]) &req[ntrec]; 150 trecno = 0; 151 asize += writesize/density; 152 asize += 7; /* inter-record gap (why fixed?) */ 153 blockswritten += ntrec; 154 if (!pipeout && asize > tsize) { 155 close_rewind(); 156 otape(); 157 } 158 timeest(); 159 } 160 161 rewind() 162 { 163 register int f; 164 165 if (pipeout) 166 return; 167 for (f = 0; f < SLAVES; f++) 168 close(slavefd[f]); 169 while (wait(NULL) >= 0) ; /* wait for any signals from slaves */ 170 msg("Tape rewinding\n"); 171 #ifdef RDUMP 172 rmtclose(); 173 while (rmtopen(tape, 0) < 0) 174 sleep(10); 175 rmtclose(); 176 #else 177 close(to); 178 while ((f = open(tape, 0)) < 0) 179 sleep (10); 180 close(f); 181 #endif 182 } 183 184 close_rewind() 185 { 186 rewind(); 187 if (!nogripe) { 188 msg("Change Tapes: Mount tape #%d\n", tapeno+1); 189 broadcast("CHANGE TAPES!\7\7\n"); 190 } 191 while (!query("Is the new tape mounted and ready to go?")) 192 if (query("Do you want to abort?")) 193 dumpabort(); 194 } 195 196 /* 197 * We implement taking and restoring checkpoints on the tape level. 198 * When each tape is opened, a new process is created by forking; this 199 * saves all of the necessary context in the parent. The child 200 * continues the dump; the parent waits around, saving the context. 201 * If the child returns X_REWRITE, then it had problems writing that tape; 202 * this causes the parent to fork again, duplicating the context, and 203 * everything continues as if nothing had happened. 204 */ 205 206 otape() 207 { 208 int parentpid; 209 int childpid; 210 int status; 211 int waitpid; 212 int interrupt(); 213 214 parentpid = getpid(); 215 216 restore_check_point: 217 signal(SIGINT, interrupt); 218 /* 219 * All signals are inherited... 220 */ 221 childpid = fork(); 222 if (childpid < 0) { 223 msg("Context save fork fails in parent %d\n", parentpid); 224 Exit(X_ABORT); 225 } 226 if (childpid != 0) { 227 /* 228 * PARENT: 229 * save the context by waiting 230 * until the child doing all of the work returns. 231 * don't catch the interrupt 232 */ 233 signal(SIGINT, SIG_IGN); 234 #ifdef TDEBUG 235 msg("Tape: %d; parent process: %d child process %d\n", 236 tapeno+1, parentpid, childpid); 237 #endif TDEBUG 238 while ((waitpid = wait(&status)) != childpid) 239 msg("Parent %d waiting for child %d has another child %d return\n", 240 parentpid, childpid, waitpid); 241 if (status & 0xFF) { 242 msg("Child %d returns LOB status %o\n", 243 childpid, status&0xFF); 244 } 245 status = (status >> 8) & 0xFF; 246 #ifdef TDEBUG 247 switch(status) { 248 case X_FINOK: 249 msg("Child %d finishes X_FINOK\n", childpid); 250 break; 251 case X_ABORT: 252 msg("Child %d finishes X_ABORT\n", childpid); 253 break; 254 case X_REWRITE: 255 msg("Child %d finishes X_REWRITE\n", childpid); 256 break; 257 default: 258 msg("Child %d finishes unknown %d\n", 259 childpid, status); 260 break; 261 } 262 #endif TDEBUG 263 switch(status) { 264 case X_FINOK: 265 Exit(X_FINOK); 266 case X_ABORT: 267 Exit(X_ABORT); 268 case X_REWRITE: 269 goto restore_check_point; 270 default: 271 msg("Bad return code from dump: %d\n", status); 272 Exit(X_ABORT); 273 } 274 /*NOTREACHED*/ 275 } else { /* we are the child; just continue */ 276 #ifdef TDEBUG 277 sleep(4); /* allow time for parent's message to get out */ 278 msg("Child on Tape %d has parent %d, my pid = %d\n", 279 tapeno+1, parentpid, getpid()); 280 #endif 281 #ifdef RDUMP 282 while ((to = rmtopen(tape, 2)) < 0) 283 #else 284 while ((to = pipeout ? 1 : creat(tape, 0666)) < 0) 285 #endif 286 if (!query("Cannot open tape. Do you want to retry the open?")) 287 dumpabort(); 288 289 enslave(); /* Share open tape file descriptor with slaves */ 290 291 asize = 0; 292 tapeno++; /* current tape sequence */ 293 newtape++; /* new tape signal */ 294 spcl.c_volume++; 295 spcl.c_type = TS_TAPE; 296 spclrec(); 297 if (tapeno > 1) 298 msg("Tape %d begins with blocks from ino %d\n", 299 tapeno, ino); 300 } 301 } 302 303 dumpabort() 304 { 305 if (master != 0 && master != getpid()) 306 kill(master, SIGIOT); 307 msg("The ENTIRE dump is aborted.\n"); 308 Exit(X_ABORT); 309 } 310 311 Exit(status) 312 { 313 #ifdef TDEBUG 314 msg("pid = %d exits with status %d\n", getpid(), status); 315 #endif TDEBUG 316 exit(status); 317 } 318 319 #define OK 020 320 char tok = OK; 321 322 enslave() 323 { 324 int prev[2], next[2], cmd[2]; /* file descriptors for pipes */ 325 int i, j, ret, slavepid; 326 327 master = getpid(); 328 signal(SIGPIPE, dumpabort); 329 signal(SIGIOT, tperror); /* SIGIOT asks for restart from checkpoint */ 330 pipe(prev); 331 for (i = rotor = 0; i < SLAVES; ++i) { 332 if ((i < SLAVES - 1 && pipe(next) < 0) || pipe(cmd) < 0 333 || (slavepid = fork()) < 0) { 334 perror(" DUMP: too many slaves"); 335 dumpabort(); 336 } 337 if (i >= SLAVES - 1) 338 next[1] = prev[1]; /* Last slave loops back */ 339 slavefd[i] = cmd[1]; 340 if (slavepid == 0) { /* Slave starts up here */ 341 for (j = 0; j <= i; j++) 342 close(slavefd[j]); 343 if (i < SLAVES - 1) { 344 close(prev[1]); 345 close(next[0]); 346 } else { /* Insert initial token */ 347 if ((ret = write(next[1], &tok, 1)) != 1) 348 ringerr(ret, "cannot start token"); 349 } 350 doslave(i, cmd[0], prev[0], next[1]); 351 close(next[1]); 352 j = read(prev[0], &tok, 1); /* Eat the final token */ 353 #ifdef RDUMP /* Read remaining acknowledges */ 354 for (; j > 0 && (tok &~ OK) > 0; tok--) { 355 if (rmtwrite2() != writesize && (tok & OK)) { 356 kill(master, SIGIOT); 357 tok &= ~OK; 358 } 359 } 360 #endif 361 Exit(X_FINOK); 362 } 363 close(cmd[0]); 364 close(next[1]); 365 close(prev[0]); 366 prev[0] = next[0]; 367 } 368 master = 0; 369 } 370 371 /* 372 * Somebody must have died, should never happen 373 */ 374 ringerr(code, msg, a1, a2) 375 int code; 376 char *msg; 377 int a1, a2; 378 { 379 char buf[BUFSIZ]; 380 381 fprintf(stderr, " DUMP: "); 382 sprintf(buf, msg, a1, a2); 383 if (code < 0) 384 perror(msg); 385 else if (code == 0) 386 fprintf(stderr, "%s: unexpected EOF\n", buf); 387 else 388 fprintf(stderr, "%s: code %d\n", buf, code); 389 kill(master, SIGPIPE); 390 Exit(X_ABORT); 391 } 392 393 int childnum; 394 sigpipe() 395 { 396 397 ringerr(childnum, "SIGPIPE raised"); 398 } 399 400 doslave(num, cmd, prev, next) 401 int num, cmd, prev, next; 402 { 403 int ret; 404 405 tmsg("slave %d\n", num); 406 signal(SIGINT, SIG_IGN); /* Master handles it */ 407 signal(SIGTERM, SIG_IGN); 408 signal(SIGPIPE, sigpipe); 409 childnum = num; 410 close(fi); 411 if ((fi = open(disk, 0)) < 0) { /* Need our own seek pointer */ 412 perror(" DUMP: can't reopen disk"); 413 kill(master, SIGPIPE); 414 Exit(X_ABORT); 415 } 416 while ((ret = readpipe(cmd, req, reqsiz)) == reqsiz) { 417 register struct req *p = req; 418 for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) { 419 if (p->dblk) { 420 tmsg("%d READS %d\n", num, p->count); 421 bread(p->dblk, tblock[trecno], 422 p->count * TP_BSIZE); 423 } else { 424 tmsg("%d PIPEIN %d\n", num, p->count); 425 if (p->count != 1) 426 ringerr(11, "%d PIPEIN %d", num, 427 p->count); 428 if (readpipe(cmd, tblock[trecno], TP_BSIZE) != TP_BSIZE) 429 senderr(); 430 } 431 } 432 if ((ret = read(prev, &tok, 1)) != 1) 433 ringerr(ret, "read token"); /* Wait your turn */ 434 tmsg("%d WRITE\n", num); 435 #ifdef RDUMP 436 if (tok & OK) { 437 rmtwrite0(writesize); 438 rmtwrite1(tblock[0], writesize); 439 tok++; /* Number of writes in progress */ 440 } 441 if (tok > (LAG|OK) && (--tok, rmtwrite2() != writesize)) { 442 #else 443 if ((tok & OK) && 444 write(to, tblock[0], writesize) != writesize) { 445 perror(tape); 446 #endif 447 kill(master, SIGIOT); /* restart from checkpoint */ 448 tok &= ~OK; 449 } 450 if ((ret = write(next, &tok, 1)) != 1) 451 ringerr(ret, "write token"); /* Next slave's turn */ 452 } 453 if (ret != 0) 454 ringerr(ret, "partial record?"); 455 tmsg("%d CLOSE\n", num); 456 } 457 458 /* 459 * Since a read from a pipe may not return all we asked for 460 * we must loop until we get all we need 461 */ 462 readpipe(fd, buf, cnt) 463 int fd; 464 char *buf; 465 int cnt; 466 { 467 int rd, got; 468 469 for (rd = cnt; rd > 0; rd -= got) { 470 got = read(fd, buf, rd); 471 if (got < 0) 472 return (got); 473 if (got == 0) 474 return (cnt - rd); 475 buf += got; 476 } 477 return (cnt); 478 } 479