2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Author: James da Silva, Systems Design and Analysis Group
24 * Computer Science Department
25 * University of Maryland at College Park
28 * $Id: tapetype.c,v 1.3.2.3.4.3.2.9 2003/11/28 12:34:52 martinea Exp $
30 * tests a tape in a given tape unit and prints a tapetype entry for
36 #define NBLOCKS 32 /* number of random blocks */
40 static char *sProgName;
44 static int blockkb = 32;
47 static char *randombytes = (char *) NULL;
50 /* If the C library does not define random(), try to use rand() by
51 defining USE_RAND, but then make sure you are not using hardware
52 compression, because the low-order bits of rand() may not be that
54 #define random() rand()
55 #define srandom(seed) srand(seed)
58 static void allocrandombytes() {
62 if (randombytes == (char *)NULL) {
63 #if defined(HAVE_GETPAGESIZE)
64 page_size = getpagesize();
68 j = (NBLOCKS * blocksize) + page_size; /* buffer space plus one page */
69 j = am_round(j, page_size); /* even number of pages */
71 i = (p - (char *)0) & (page_size - 1); /* page boundary offset */
73 randombytes = p + page_size - i; /* round up to page boundary */
75 randombytes = p; /* alloc already on boundary */
80 static void initnotrandombytes() {
85 j =NBLOCKS * blocksize;
87 for(i=0; i < j; ++i) {
88 *p++ = (char) (i % 256);
92 static void initrandombytes() {
97 j = NBLOCKS * blocksize;
99 for(i=0; i < j; ++i) {
100 *p++ = (char)random();
104 static char *getrandombytes() {
105 static int counter = 0;
107 return randombytes + ((counter++ % NBLOCKS) * blocksize);
110 static int short_write;
117 if ((w = tapefd_write(fd, getrandombytes(), blocksize)) == blocksize) {
129 /* returns number of blocks actually written */
130 size_t writeblocks(int fd, size_t nblks)
134 while (blks < nblks) {
135 if (! writeblock(fd)) {
147 fputs("usage: ", stderr);
148 fputs(sProgName, stderr);
149 fputs(" -h", stderr);
150 fputs(" [-c]", stderr);
151 fputs(" [-b blocksize]", stderr);
152 fputs(" [-e estsize]", stderr);
153 fputs(" [-f tapedev]", stderr);
154 fputs(" [-t typename]", stderr);
162 -h display this message\n\
163 -c run hardware compression detection test only\n\
164 -b blocksize record block size (default: 32k)\n\
165 -e estsize estimated tape size (default: 1g == 1024m)\n\
166 -f tapedev tape device name (default: $TAPE)\n\
167 -t typename tapetype name (default: unknown-tapetype)\n\
169 Note: disable hardware compression when running this program.\n\
176 void show_progress(blocks, files)
177 size_t *blocks, *files;
179 fprintf(stderr, "wrote %ld %dKb block%s in %ld file%s",
180 (long)*blocks, blockkb, (*blocks == 1) ? "" : "s",
181 (long)*files, (*files == 1) ? "" : "s");
185 void do_pass(size, blocks, files, seconds)
186 size_t size, *blocks, *files;
193 if (tapefd_rewind(fd) == -1) {
194 fprintf(stderr, "%s: could not rewind %s: %s\n",
195 sProgName, tapedev, strerror(errno));
203 if ((blks = writeblocks(fd, size)) <= 0 || tapefd_weof(fd, 1) != 0)
209 show_progress(blocks, files);
217 fprintf(stderr, "%s: could not write any data in this pass: %s\n",
218 sProgName, short_write ? "short write" : strerror(save_errno));
224 * Just in case time warped backward or the device is really, really
225 * fast (e.g. /dev/null testing).
229 *seconds = end - start;
234 show_progress(blocks, files);
235 fprintf(stderr, " in %ld second%s (%s)\n",
236 (long)*seconds, ((long)*seconds == 1) ? "" : "s",
237 short_write ? "short write" : strerror(save_errno));
241 void do_pass0(size, seconds, dorewind)
250 if (dorewind && tapefd_rewind(fd) == -1) {
251 fprintf(stderr, "%s: could not rewind %s: %s\n",
252 sProgName, tapedev, strerror(errno));
258 blks = writeblocks(fd, size);
266 fprintf(stderr, "%s: could not write any data in this pass: %s\n",
267 sProgName, short_write ? "short write" : strerror(save_errno));
273 * Just in case time warped backward or the device is really, really
274 * fast (e.g. /dev/null testing).
278 *seconds = end - start;
287 size_t pass1blocks = 0;
288 size_t pass2blocks = 0;
292 size_t pass1files = 0;
293 size_t pass2files = 0;
309 int comprtstonly = 0;
311 if ((sProgName = strrchr(*argv, '/')) == NULL) {
317 estsize = 1024 * 1024; /* assume 1 GByte for now */
318 tapedev = getenv("TAPE");
319 typename = "unknown-tapetype";
321 while ((ch = getopt(argc, argv, "b:e:f:t:hc")) != EOF) {
324 blockkb = strtol(optarg, &suffix, 0);
325 if (*suffix == '\0' || *suffix == 'k' || *suffix == 'K') {
326 } else if (*suffix == 'm' || *suffix == 'M') {
328 } else if (*suffix == 'g' || *suffix == 'G') {
329 blockkb *= 1024 * 1024;
331 fprintf(stderr, "%s: unknown size suffix \'%c\'\n", sProgName, *suffix);
336 estsize = strtol(optarg, &suffix, 0);
337 if (*suffix == '\0' || *suffix == 'k' || *suffix == 'K') {
338 } else if (*suffix == 'm' || *suffix == 'M') {
340 } else if (*suffix == 'g' || *suffix == 'G') {
341 estsize *= 1024 * 1024;
343 fprintf(stderr, "%s: unknown size suffix \'%c\'\n", sProgName, *suffix);
348 tapedev = stralloc(optarg);
351 typename = stralloc(optarg);
361 fprintf(stderr, "%s: unknown option \'%c\'\n", sProgName, ch);
362 /* fall through to ... */
369 blocksize = blockkb * 1024;
371 if (tapedev == NULL || optind < argc) {
376 fd = tape_open(tapedev, O_RDWR);
378 fprintf(stderr, "%s: could not open %s: %s\n",
379 sProgName, tapedev, strerror(errno));
383 do_tty = isatty(fileno(stderr));
386 * Estimate pass: write twice a small file, once with compressable
387 * data and once with uncompressable data.
388 * The theory is that if the drive is in hardware compression mode
389 * we notice a significant difference in writing speed between the two
390 * (at least if we can provide data as fast the tape streams).
393 initnotrandombytes();
395 fprintf(stderr, "Estimate phase 1...");
396 pass0size = 8 * 1024 / blockkb;
400 * To get accurate results, we should write enough data
401 * so that rewind/start/stop time is small compared to
402 * the total time; let's take 10%.
403 * The timer has a 1 sec granularity, so the test
404 * should take at least 10 seconds to measure a
405 * difference with 10% accuracy; let's take 25 seconds.
407 while (pass1time < 25 || ((100*(pass2time-pass1time)/pass2time) >= 10) ) {
408 if (pass1time != 0) {
416 * first a dummy pass to rewind, stop, start and
417 * get drive streaming, then do the real timing
419 do_pass0(pass0size, &pass2time, 1);
420 do_pass0(pass0size, &pass1time, 0);
421 if (pass0size >= 10 * 1024 * 1024) {
423 "\rTape device is too fast to detect hardware compression...\n");
424 break; /* avoid loops if tape is superfast or broken */
427 fprintf(stderr, "\rWriting %d Mbyte compresseable data: %d sec\n",
428 (int)(blockkb * pass0size / 1024), (int)pass1time);
431 * now generate uncompressable data and try again
437 fprintf(stderr, "Estimate phase 2...");
438 do_pass0(pass0size, &pass2time, 1); /* rewind and get drive streaming */
439 do_pass0(pass0size, &pass2time, 0);
440 fprintf(stderr, "\rWriting %d Mbyte uncompresseable data: %d sec\n",
441 (int)(blockkb * pass0size / 1024), (int)pass2time);
444 * Compute the time difference between writing the compressable and
445 * uncompressable data. If it differs more than 20%, then warn
446 * user that the tape drive has probably hardware compression enabled.
448 if (pass1time > pass2time) {
450 * Strange! I would expect writing compresseable data to be
451 * much faster (or about equal, if hardware compression is disabled)
455 timediff = pass2time - pass1time;
457 if (((100 * timediff) / pass2time) >= 20) { /* 20% faster? */
458 fprintf(stderr, "WARNING: Tape drive has hardware compression enabled\n");
463 * Inform about estimated time needed to run the remaining of this program
465 fprintf(stderr, "Estimated time to write 2 * %d Mbyte: ", estsize / 1024);
466 pass1time = (time_t)(2.0 * pass2time * estsize / (1.0 * pass0size * blockkb));
467 /* avoid overflow and underflow by doing math in floating point */
468 fprintf(stderr, "%ld sec = ", pass1time);
469 fprintf(stderr, "%ld h %ld min\n", (pass1time/3600), ((pass1time%3600) / 60));
477 * Do pass 1 -- write files that are 1% of the estimated size until error.
479 pass1size = (estsize * 0.01) / blockkb; /* 1% of estimate */
481 pass1size = 2; /* strange end case */
483 do_pass(pass1size, &pass1blocks, &pass1files, &pass1time);
486 * Do pass 2 -- write smaller files until error.
488 pass2size = pass1size / 2;
489 do_pass(pass2size, &pass2blocks, &pass2files, &pass2time);
492 * Compute the size of a filemark as the difference in data written
493 * between pass 1 and pass 2 divided by the difference in number of
494 * file marks written between pass 1 and pass 2. Note that we have
495 * to be careful in case size_t is unsigned (i.e. do not subtract
496 * things and then check for less than zero).
498 if (pass1blocks <= pass2blocks) {
500 * If tape marks take up space, there should be fewer blocks in pass
501 * 2 than in pass 1 since we wrote twice as many tape marks. But
502 * odd things happen, so make sure the result does not go negative.
506 blockdiff = pass1blocks - pass2blocks;
508 if (pass2files <= pass1files) {
510 * This should not happen, but just in case ...
514 filediff = pass2files - pass1files;
516 filemark = blockdiff * blockkb / filediff;
519 * Compute the length as the average of the two pass sizes including
522 size = ((pass1blocks * blockkb + filemark * pass1files)
523 + (pass2blocks * blockkb + filemark * pass2files)) / 2;
524 if (size >= 1024 * 1024 * 1000) {
526 sizeunits = "gbytes";
527 } else if (size >= 1024 * 1000) {
529 sizeunits = "mbytes";
531 sizeunits = "kbytes";
535 * Compute the speed as the average of the two passes.
537 speed = (((double)pass1blocks * blockkb / pass1time)
538 + ((double)pass2blocks * blockkb / pass2time)) / 2;
543 printf("define tapetype %s {\n", typename);
544 printf(" comment \"just produced by tapetype prog (hardware compression %s)\"\n",
545 hwcompr ? "on" : "off");
546 printf(" length %ld %s\n", (long)size, sizeunits);
547 printf(" filemark %ld kbytes\n", filemark);
548 printf(" speed %ld kps\n", speed);
551 if (tapefd_rewind(fd) == -1) {
552 fprintf(stderr, "%s: could not rewind %s: %s\n",
553 sProgName, tapedev, strerror(errno));
557 if (tapefd_close(fd) == -1) {
558 fprintf(stderr, "%s: could not close %s: %s\n",
559 sProgName, tapedev, strerror(errno));