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