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