X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fstored%2Fbtape.c;h=b4fbf4f23924c1dfe3fc090378f7ec9c2d7c567b;hb=79822b6b171cc2af23c6f3cd1608c59e15897a84;hp=61c14e5aafa4e87a0350308fde4ece91afc2938c;hpb=6bca97aa2a205e95c82ac1900a59f50344658200;p=bacula%2Fbacula diff --git a/bacula/src/stored/btape.c b/bacula/src/stored/btape.c index 61c14e5aaf..b4fbf4f239 100644 --- a/bacula/src/stored/btape.c +++ b/bacula/src/stored/btape.c @@ -1,12 +1,12 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2000-2008 Free Software Foundation Europe e.V. + Copyright (C) 2000-2010 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. This program is Free Software; you can redistribute it and/or - modify it under the terms of version two of the GNU General Public + modify it under the terms of version three of the GNU Affero General Public License as published by the Free Software Foundation and included in the file LICENSE. @@ -15,7 +15,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Affero General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -37,8 +37,6 @@ * Note, this program reads stored.conf, and will only * talk to devices that are configured. * - * Version $Id$ - * */ #include "bacula.h" @@ -74,7 +72,9 @@ static uint32_t btape_state_level = 2; DEVICE *dev = NULL; DCR *dcr; DEVRES *device = NULL; +int exit_code = 0; +#define REC_SIZE 32768 /* Forward referenced subroutines */ static void do_tape_cmds(); @@ -99,7 +99,7 @@ static void set_volume_name(const char *VolName, int volnum); static void rawfill_cmd(); static bool open_the_device(); static void autochangercmd(); -static void do_unfill(); +static bool do_unfill(); /* Static variables */ @@ -123,7 +123,6 @@ static int stop = 0; static uint64_t vol_size; static uint64_t VolBytes; static time_t now; -static double kbs; static int32_t file_index; static int end_of_tape = 0; static uint32_t LastBlock = 0; @@ -172,6 +171,7 @@ int main(int margc, char *margv[]) bindtextdomain("bacula", LOCALEDIR); textdomain("bacula"); init_stack_dump(); + lmgr_init_thread(); /* Sanity checks */ if (TAPE_BSIZE % B_DEV_BSIZE != 0 || TAPE_BSIZE / B_DEV_BSIZE == 0) { @@ -305,15 +305,13 @@ int main(int margc, char *margv[]) } dcr = jcr->dcr; if (!open_the_device()) { - goto terminate; + exit(1); } Dmsg0(200, "Do tape commands\n"); do_tape_cmds(); -terminate: - terminate_btape(0); - return 0; + terminate_btape(exit_code); } static void terminate_btape(int stat) @@ -367,6 +365,101 @@ static void terminate_btape(int stat) exit(stat); } + +btime_t total_time=0; +uint64_t total_size=0; + +static void init_total_speed() +{ + total_size = 0; + total_time = 0; +} + +static void print_total_speed() +{ + char ec1[50], ec2[50]; + uint64_t rate = total_size / total_time; + Pmsg2(000, _("Total Volume bytes=%sB. Total Write rate = %sB/s\n"), + edit_uint64_with_suffix(total_size, ec1), + edit_uint64_with_suffix(rate, ec2)); +} + +static void init_speed() +{ + time(&jcr->run_time); /* start counting time for rates */ + jcr->JobBytes=0; +} + +static void print_speed(uint64_t bytes) +{ + char ec1[50], ec2[50]; + uint64_t rate; + + now = time(NULL); + now -= jcr->run_time; + if (now <= 0) { + now = 1; /* don't divide by zero */ + } + + total_time += now; + total_size += bytes; + + rate = bytes / now; + Pmsg2(000, _("Volume bytes=%sB. Write rate = %sB/s\n"), + edit_uint64_with_suffix(bytes, ec1), + edit_uint64_with_suffix(rate, ec2)); +} + +/* + * Helper that fill a buffer with random data or not + */ +typedef enum { + FILL_RANDOM, + FILL_ZERO +} fill_mode_t; + +static void fill_buffer(fill_mode_t mode, char *buf, uint32_t len) +{ + int fd; + switch (mode) { + case FILL_RANDOM: + fd = open("/dev/urandom", O_RDONLY); + if (fd != -1) { + read(fd, buf, len); + close(fd); + } else { + uint32_t *p = (uint32_t *)buf; + srandom(time(NULL)); + for (uint32_t i=0; iblock; DEV_RECORD *rec; - int stat = 0; + bool rc = false; int len; if (!(dev->capabilities & CAP_BSR)) { Pmsg0(-1, _("Skipping read backwards test because BSR turned off.\n")); - return 0; + return true; } Pmsg0(-1, _("\n=== Write, backup, and re-read test ===\n\n" @@ -781,11 +874,11 @@ static int re_read_block_test() Pmsg0(0, _("\nBlock re-read correct. Test succeeded!\n")); Pmsg0(-1, _("=== End Write, backup, and re-read test ===\n\n")); - stat = 1; + rc = true; bail_out: free_record(rec); - if (stat == 0) { + if (!rc) { Pmsg0(0, _("This is not terribly serious since Bacula only uses\n" "this function to verify the last block written to the\n" "tape. Bacula will skip the last block verification\n" @@ -793,38 +886,252 @@ bail_out: "Backward Space Record = No\n\n" "to your Storage daemon's Device resource definition.\n")); } - return stat; + return rc; } +static bool speed_test_raw(fill_mode_t mode, uint64_t nb_gb, uint32_t nb) +{ + DEV_BLOCK *block = dcr->block; + int stat; + uint32_t block_num = 0; + int my_errno; + char ed1[200]; + nb_gb *= 1024*1024*1024; /* convert size from nb to GB */ + + init_total_speed(); + fill_buffer(mode, block->buf, block->buf_len); + + Pmsg3(0, _("Begin writing %i files of %sB with raw blocks of %u bytes.\n"), + nb, edit_uint64_with_suffix(nb_gb, ed1), block->buf_len); + + for (uint32_t j=0; jJobBytes < nb_gb; ) { + stat = dev->d_write(dev->fd(), block->buf, block->buf_len); + if (stat == (int)block->buf_len) { + if ((block_num++ % 500) == 0) { + printf("+"); + fflush(stdout); + } + + mix_buffer(mode, block->buf, block->buf_len); + + jcr->JobBytes += stat; + + } else { + my_errno = errno; + printf("\n"); + berrno be; + printf(_("Write failed at block %u. stat=%d ERR=%s\n"), block_num, + stat, be.bstrerror(my_errno)); + return false; + } + } + printf("\n"); + weofcmd(); + print_speed(jcr->JobBytes); + } + print_total_speed(); + printf("\n"); + return true; +} + + +static bool speed_test_bacula(fill_mode_t mode, uint64_t nb_gb, uint32_t nb) +{ + DEV_BLOCK *block = dcr->block; + char ed1[200]; + DEV_RECORD *rec; + uint64_t last_bytes = dev->VolCatInfo.VolCatBytes; + uint64_t written=0; + + nb_gb *= 1024*1024*1024; /* convert size from nb to GB */ + + init_total_speed(); + + empty_block(block); + rec = new_record(); + rec->data = check_pool_memory_size(rec->data, block->buf_len); + rec->data_len = block->buf_len-100; + + fill_buffer(mode, rec->data, rec->data_len); + + Pmsg3(0, _("Begin writing %i files of %sB with blocks of %u bytes.\n"), + nb, edit_uint64_with_suffix(nb_gb, ed1), block->buf_len); + + for (uint32_t j=0; jBlockNumber % 500) == 0) { + printf("+"); + fflush(stdout); + } + written += dev->VolCatInfo.VolCatBytes - last_bytes; + last_bytes = dev->VolCatInfo.VolCatBytes; + mix_buffer(mode, rec->data, rec->data_len); + } + printf("\n"); + weofcmd(); + print_speed(written); + } + print_total_speed(); + printf("\n"); + free_record(rec); + return true; + +bail_out: + free_record(rec); + return false; +} + +/* TODO: use UAContext */ +static int btape_find_arg(const char *keyword) +{ + for (int i=1; i 0) { + file_size = atoi(argv[i]); + if (file_size > 100) { + Pmsg0(0, _("The file_size is too big, stop this test with Ctrl-c.\n")); + } + } + + i = btape_find_arg("nb_file"); + if (i > 0) { + nb_file = atoi(argv[i]); + } + + if (btape_find_arg("skip_zero") > 0) { + do_zero = false; + } + + if (btape_find_arg("skip_random") > 0) { + do_random = false; + } + + if (btape_find_arg("skip_raw") > 0) { + do_raw = false; + } + + if (btape_find_arg("skip_block") > 0) { + do_block = false; + } + + if (do_raw) { + dev->rewind(dcr); + if (do_zero) { + Pmsg0(0, _("Test with zero data, should give the " + "maximum throughput.\n")); + if (file_size) { + ok(speed_test_raw(FILL_ZERO, file_size, nb_file)); + } else { + ok(speed_test_raw(FILL_ZERO, 1, nb_file)); + ok(speed_test_raw(FILL_ZERO, 2, nb_file)); + ok(speed_test_raw(FILL_ZERO, 4, nb_file)); + } + } + + if (do_random) { + Pmsg0(0, _("Test with random data, should give the minimum " + "throughput.\n")); + if (file_size) { + ok(speed_test_raw(FILL_RANDOM, file_size, nb_file)); + } else { + ok(speed_test_raw(FILL_RANDOM, 1, nb_file)); + ok(speed_test_raw(FILL_RANDOM, 2, nb_file)); + ok(speed_test_raw(FILL_RANDOM, 4, nb_file)); + } + } + } + + if (do_block) { + dev->rewind(dcr); + if (do_zero) { + Pmsg0(0, _("Test with zero data and bacula block structure.\n")); + if (file_size) { + ok(speed_test_bacula(FILL_ZERO, file_size, nb_file)); + } else { + ok(speed_test_bacula(FILL_ZERO, 1, nb_file)); + ok(speed_test_bacula(FILL_ZERO, 2, nb_file)); + ok(speed_test_bacula(FILL_ZERO, 4, nb_file)); + } + } + + if (do_random) { + Pmsg0(0, _("Test with random data, should give the minimum " + "throughput.\n")); + if (file_size) { + ok(speed_test_bacula(FILL_RANDOM, file_size, nb_file)); + } else { + ok(speed_test_bacula(FILL_RANDOM, 1, nb_file)); + ok(speed_test_bacula(FILL_RANDOM, 2, nb_file)); + ok(speed_test_bacula(FILL_RANDOM, 4, nb_file)); + } + } + } +} + +const int num_recs = 10000; + +static bool write_two_files() { DEV_BLOCK *block; DEV_RECORD *rec; - int stat = 0; int len, i, j; int *p; + bool rc = false; /* bad return code */ - Pmsg0(-1, _("\n=== Write, rewind, and re-read test ===\n\n" - "I'm going to write 1000 records and an EOF\n" - "then write 1000 records and an EOF, then rewind,\n" + Pmsg2(-1, _("\n=== Write, rewind, and re-read test ===\n\n" + "I'm going to write %d records and an EOF\n" + "then write %d records and an EOF, then rewind,\n" "and re-read the data to verify that it is correct.\n\n" - "This is an *essential* feature ...\n\n")); + "This is an *essential* feature ...\n\n"), num_recs, num_recs); + block = dcr->block; + empty_block(block); rec = new_record(); + rec->data = check_pool_memory_size(rec->data, block->buf_len); + rec->data_len = block->buf_len-100; + len = rec->data_len/sizeof(i); + if (!dev->rewind(dcr)) { Pmsg1(0, _("Bad status from rewind. ERR=%s\n"), dev->bstrerror()); goto bail_out; } - rec->data = check_pool_memory_size(rec->data, block->buf_len); - rec->data_len = block->buf_len-100; - len = rec->data_len/sizeof(i); - for (i=1; i<=1000; i++) { + + for (i=1; i<=num_recs; i++) { p = (int *)rec->data; for (j=0; jdata_len); + Pmsg2(0, _("Wrote %d blocks of %d bytes.\n"), num_recs, rec->data_len); weofcmd(); - for (i=1001; i<=2000; i++) { + for (i=num_recs+1; i<=2*num_recs; i++) { p = (int *)rec->data; for (j=0; jdata_len); + Pmsg2(0, _("Wrote %d blocks of %d bytes.\n"), num_recs, rec->data_len); weofcmd(); if (dev->has_cap(CAP_TWOEOF)) { weofcmd(); } + rc = true; + +bail_out: + free_record(rec); + if (!rc) { + exit_code = 1; + } + return rc; + +} + +/* + * This test writes Bacula blocks to the tape in + * several files. It then rewinds the tape and attepts + * to read these blocks back checking the data. + */ +static bool write_read_test() +{ + DEV_BLOCK *block; + DEV_RECORD *rec; + bool rc = false; + int len, i, j; + int *p; + + rec = new_record(); + + if (!write_two_files()) { + goto bail_out; + } + + block = dcr->block; + empty_block(block); + if (!dev->rewind(dcr)) { Pmsg1(0, _("Bad status from rewind. ERR=%s\n"), dev->bstrerror()); goto bail_out; } else { Pmsg0(0, _("Rewind OK.\n")); } - for (i=1; i<=2000; i++) { + + rec->data = check_pool_memory_size(rec->data, block->buf_len); + rec->data_len = block->buf_len-100; + len = rec->data_len/sizeof(i); + + /* Now read it back */ + for (i=1; i<=2*num_recs; i++) { read_again: if (!read_block_from_dev(dcr, NO_BLOCK_NUMBER_CHECK)) { berrno be; if (dev_state(dev, ST_EOF)) { Pmsg0(-1, _("Got EOF on tape.\n")); - if (i == 1001) { + if (i == num_recs+1) { goto read_again; } } @@ -893,16 +1239,19 @@ read_again: } p++; } - if (i == 1000 || i == 2000) { - Pmsg0(-1, _("1000 blocks re-read correctly.\n")); + if (i == num_recs || i == 2*num_recs) { + Pmsg1(-1, _("%d blocks re-read correctly.\n"), num_recs); } } Pmsg0(-1, _("=== Test Succeeded. End Write, rewind, and re-read test ===\n\n")); - stat = 1; + rc = true; bail_out: free_record(rec); - return stat; + if (!rc) { + exit_code = 1; + } + return rc; } /* @@ -910,67 +1259,26 @@ bail_out: * several files. It then rewinds the tape and attepts * to read these blocks back checking the data. */ -static int position_test() +static bool position_test() { DEV_BLOCK *block = dcr->block; DEV_RECORD *rec; - int stat = 0; - int len, i, j; - bool ok = true; + bool rc = false; + int len, j; + bool more = true; int recno = 0; int file = 0, blk = 0; int *p; bool got_eof = false; - Pmsg0(-1, _("\n=== Write, rewind, and position test ===\n\n" - "I'm going to write 1000 records and an EOF\n" - "then write 1000 records and an EOF, then rewind,\n" - "and position to a few blocks and verify that it is correct.\n\n" - "This is an *essential* feature ...\n\n")); + Pmsg0(0, _("Block position test\n")); + block = dcr->block; empty_block(block); rec = new_record(); - if (!dev->rewind(dcr)) { - Pmsg1(0, _("Bad status from rewind. ERR=%s\n"), dev->bstrerror()); - goto bail_out; - } rec->data = check_pool_memory_size(rec->data, block->buf_len); rec->data_len = block->buf_len-100; - len = rec->data_len/sizeof(i); - for (i=1; i<=1000; i++) { - p = (int *)rec->data; - for (j=0; jdata_len); - weofcmd(); - for (i=1001; i<=2000; i++) { - p = (int *)rec->data; - for (j=0; jdata_len); - weofcmd(); - if (dev->has_cap(CAP_TWOEOF)) { - weofcmd(); - } + len = rec->data_len/sizeof(j); + if (!dev->rewind(dcr)) { Pmsg1(0, _("Bad status from rewind. ERR=%s\n"), dev->bstrerror()); goto bail_out; @@ -978,8 +1286,11 @@ static int position_test() Pmsg0(0, _("Rewind OK.\n")); } - while(ok) { + while (more) { /* Set up next item to read based on where we are */ + /* At each step, recno is what we print for the "block number" + * and file, blk are the real positions to go to. + */ switch (recno) { case 0: recno = 5; @@ -992,27 +1303,27 @@ static int position_test() blk = 200; break; case 201: - recno = 1000; + recno = num_recs; file = 0; - blk = 999; + blk = num_recs-1; break; - case 1000: - recno = 1001; + case num_recs: + recno = num_recs+1; file = 1; blk = 0; break; - case 1001: - recno = 1601; + case num_recs+1: + recno = num_recs+601; file = 1; blk = 600; break; - case 1601: - recno = 2000; + case num_recs+601: + recno = 2*num_recs; file = 1; - blk = 999; + blk = num_recs-1; break; - case 2000: - ok = false; + case 2*num_recs: + more = false; continue; } Pmsg2(-1, _("Reposition to file:block %d:%d\n"), file, blk); @@ -1062,11 +1373,11 @@ read_again: Pmsg1(-1, _("Block %d re-read correctly.\n"), recno); } Pmsg0(-1, _("=== Test Succeeded. End Write, rewind, and re-read test ===\n\n")); - stat = 1; + rc = true; bail_out: free_record(rec); - return stat; + return rc; } @@ -1289,7 +1600,7 @@ static void autochangercmd() * This test assumes that the append test has been done, * then it tests the fsf function. */ -static int fsf_test() +static bool fsf_test() { bool set_off = false; @@ -1371,7 +1682,7 @@ test_again: goto bail_out; } Pmsg0(-1, _("\n=== End Forward space files test ===\n\n")); - return 1; + return true; bail_out: Pmsg0(-1, _("\nThe forward space file test failed.\n")); @@ -1386,7 +1697,7 @@ bail_out: "Some systems, e.g. OpenBSD, require you to set\n" " Use MTIOCGET= no\n" "in your device resource. Use with caution.\n")); - return -2; + return false; } @@ -1402,9 +1713,11 @@ static void testcmd() int stat; if (!write_read_test()) { + exit_code = 1; return; } if (!position_test()) { + exit_code = 1; return; } @@ -1467,6 +1780,7 @@ failed: "Some systems, e.g. OpenBSD, require you to set\n" " Use MTIOCGET= no\n" "in your device resource. Use with caution.\n")); + exit_code = 1; return; } @@ -1490,10 +1804,14 @@ all_done: "the tape.\n\n")); if (stat == 1) { - re_read_block_test(); + if (!re_read_block_test()) { + exit_code = 1; + } } - fsf_test(); /* do fast forward space file test */ + if (!fsf_test()) { /* do fast forward space file test */ + exit_code = 1; + } autochanger_test(); /* do autochanger test */ @@ -1784,14 +2102,14 @@ static void scan_blocks() blocks++; tot_blocks++; bytes += block->block_len; - Dmsg6(100, "Blk_blk=%u dev_blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n", - block->BlockNumber, dev->block_num, block->block_len, block->BlockVer, + Dmsg7(100, "Blk_blk=%u file,blk=%u,%u blen=%u bVer=%d SessId=%u SessTim=%u\n", + block->BlockNumber, dev->file, dev->block_num, block->block_len, block->BlockVer, block->VolSessionId, block->VolSessionTime); if (verbose == 1) { DEV_RECORD *rec = new_record(); read_record_from_block(dcr, block, rec); - Pmsg8(-1, _("Blk_block: %u dev_blk=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"), - block->BlockNumber, dev->block_num, block->block_len, + Pmsg9(-1, _("Block=%u file,blk=%u,%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"), + block->BlockNumber, dev->file, dev->block_num, block->block_len, FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime, stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len); rec->remainder = 0; @@ -1828,11 +2146,12 @@ static void fillcmd() { DEV_RECORD rec; DEV_BLOCK *block = dcr->block; - char ec1[50]; + char ec1[50], ec2[50]; char buf1[100], buf2[100]; - int fd; - uint32_t i; + uint64_t write_eof; + uint64_t rate; uint32_t min_block_size; + int fd; struct tm tm; ok = true; @@ -1841,8 +2160,9 @@ static void fillcmd() last_file = 0; last_block_num = 0; BlockNumber = 0; + exit_code = 0; - Pmsg0(-1, _("\n" + Pmsg1(-1, _("\n" "This command simulates Bacula writing to a tape.\n" "It requires either one or two blank tapes, which it\n" "will label and write.\n\n" @@ -1850,14 +2170,15 @@ static void fillcmd() "the tapes that are in slots 1 and 2, otherwise, you will\n" "be prompted to insert the tapes when necessary.\n\n" "It will print a status approximately\n" -"every 322 MB, and write an EOF every 3.2 GB. If you have\n" +"every 322 MB, and write an EOF every %s. If you have\n" "selected the simple test option, after writing the first tape\n" "it will rewind it and re-read the last block written.\n\n" "If you have selected the multiple tape test, when the first tape\n" "fills, it will ask for a second, and after writing a few more \n" "blocks, it will stop. Then it will begin re-reading the\n" "two tapes.\n\n" -"This may take a long time -- hours! ...\n\n")); +"This may take a long time -- hours! ...\n\n"), + edit_uint64_with_suffix(dev->max_file_size, buf1)); get_cmd(_("Do you want to run the simplified test (s) with one tape\n" "or the complete multiple tape (m) test: (s/m) ")); @@ -1869,6 +2190,7 @@ static void fillcmd() simple = false; } else { Pmsg0(000, _("Command aborted.\n")); + exit_code = 1; return; } @@ -1878,6 +2200,7 @@ static void fillcmd() /* Use fixed block size to simplify read back */ min_block_size = dev->min_block_size; dev->min_block_size = dev->max_block_size; + write_eof = dev->max_file_size / REC_SIZE; /*compute when we add EOF*/ set_volume_name("TestVolume1", 1); dir_ask_sysop_to_create_appendable_volume(dcr); dev->set_append(); /* force volume to be relabeled */ @@ -1890,6 +2213,7 @@ static void fillcmd() Dmsg0(100, "just before acquire_device\n"); if (!acquire_device_for_append(dcr)) { set_jcr_job_status(jcr, JS_ErrorTerminated); + exit_code = 1; return; } block = jcr->dcr->block; @@ -1908,24 +2232,12 @@ static void fillcmd() memset(&rec, 0, sizeof(rec)); rec.data = get_memory(100000); /* max record size */ - -#define REC_SIZE 32768 rec.data_len = REC_SIZE; /* * Put some random data in the record */ - fd = open("/dev/urandom", O_RDONLY); - if (fd != -1) { - read(fd, rec.data, rec.data_len); - close(fd); - } else { - uint32_t *p = (uint32_t *)rec.data; - srandom(time(NULL)); - for (i=0; iVolCatInfo.VolCatBytes / (1000.0 * (double)now); - Pmsg4(-1, _("Wrote blk_block=%u, dev_blk_num=%u VolBytes=%s rate=%.1f KB/s\n"), - block->BlockNumber, dev->block_num, - edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), (float)kbs); + rate = dev->VolCatInfo.VolCatBytes / now; + Pmsg5(-1, _("Wrote block=%u, file,blk=%u,%u VolBytes=%s rate=%sB/s\n"), + block->BlockNumber, dev->file, dev->block_num, + edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), + edit_uint64_with_suffix(rate, ec2)); } - /* Every 32000 blocks (approx 2GB) write an EOF. + /* Every X blocks (dev->max_file_size) write an EOF. */ - if ((block->BlockNumber % 32000) == 0) { + if ((block->BlockNumber % write_eof) == 0) { now = time(NULL); (void)localtime_r(&now, &tm); strftime(buf1, sizeof(buf1), "%H:%M:%S", &tm); Pmsg1(-1, _("%s Flush block, write EOF\n"), buf1); flush_block(block, 0); +#ifdef needed_xxx dev->weof(1); +#endif } - /* Get out after writing 10 blocks to the second tape */ - if (++BlockNumber > 10 && stop != 0) { /* get out */ + /* Get out after writing 1000 blocks to the second tape */ + if (++BlockNumber > 1000 && stop != 0) { /* get out */ + Pmsg0(000, _("Wrote 1000 blocks on second tape. Done.\n")); break; } } if (!ok) { Pmsg0(000, _("Not OK\n")); + exit_code = 1; break; } jcr->JobBytes += rec.data_len; /* increment bytes this job */ @@ -2007,30 +2322,35 @@ static void fillcmd() FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId, stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len); - /* Get out after writing 10 blocks to the second tape */ - if (BlockNumber > 10 && stop != 0) { /* get out */ + /* Get out after writing 1000 blocks to the second tape */ + if (BlockNumber > 1000 && stop != 0) { /* get out */ char ed1[50]; Pmsg1(-1, "Done writing %s records ...\n", edit_uint64_with_commas(write_count, ed1)); break; } - } + } /* end big for loop */ + if (vol_num > 1) { Dmsg0(100, "Write_end_session_label()\n"); /* Create Job status for end of session label */ if (!job_canceled(jcr) && ok) { set_jcr_job_status(jcr, JS_Terminated); } else if (!ok) { + Pmsg0(000, _("Job canceled.\n")); set_jcr_job_status(jcr, JS_ErrorTerminated); + exit_code = 1; } if (!write_session_label(dcr, EOS_LABEL)) { Pmsg1(000, _("Error writing end session label. ERR=%s\n"), dev->bstrerror()); ok = false; + exit_code = 1; } /* Write out final block of this session */ if (!write_block_to_device(dcr)) { Pmsg0(-1, _("Set ok=false after write_block_to_device.\n")); ok = false; + exit_code = 1; } Pmsg0(-1, _("Wrote End of Session label.\n")); @@ -2056,29 +2376,37 @@ static void fillcmd() write(fd, last_block2->buf, last_block2->buf_len); write(fd, first_block->buf, first_block->buf_len); close(fd); - Pmsg2(-1, _("Wrote state file last_block_num1=%d last_block_num2=%d\n"), + Pmsg2(0, _("Wrote state file last_block_num1=%d last_block_num2=%d\n"), last_block_num1, last_block_num2); } else { berrno be; - Pmsg2(-1, _("Could not create state file: %s ERR=%s\n"), buf, + Pmsg2(0, _("Could not create state file: %s ERR=%s\n"), buf, be.bstrerror()); + exit_code = 1; + ok = false; } now = time(NULL); (void)localtime_r(&now, &tm); strftime(buf1, sizeof(buf1), "%H:%M:%S", &tm); - if (simple) { - Pmsg3(-1, _("\n\n%s Done filling tape at %d:%d. Now beginning re-read of tape ...\n"), - buf1, jcr->dcr->dev->file, jcr->dcr->dev->block_num); - } - else { - Pmsg3(-1, _("\n\n%s Done filling tapes at %d:%d. Now beginning re-read of first tape ...\n"), - buf1, jcr->dcr->dev->file, jcr->dcr->dev->block_num); - } - - jcr->dcr->block = block; - do_unfill(); + if (ok) { + if (simple) { + Pmsg3(0, _("\n\n%s Done filling tape at %d:%d. Now beginning re-read of tape ...\n"), + buf1, jcr->dcr->dev->file, jcr->dcr->dev->block_num); + } else { + Pmsg3(0, _("\n\n%s Done filling tapes at %d:%d. Now beginning re-read of first tape ...\n"), + buf1, jcr->dcr->dev->file, jcr->dcr->dev->block_num); + } + jcr->dcr->block = block; + if (!do_unfill()) { + Pmsg0(000, _("do_unfill failed.\n")); + exit_code = 1; + ok = false; + } + } else { + Pmsg1(000, _("%s: Error during test.\n"), buf1); + } dev->min_block_size = min_block_size; free_memory(rec.data); } @@ -2093,6 +2421,7 @@ static void unfillcmd() { int fd; + exit_code = 0; last_block1 = new_block(dev); last_block2 = new_block(dev); first_block = new_block(dev); @@ -2113,28 +2442,38 @@ static void unfillcmd() if (state_level != btape_state_level) { Pmsg0(-1, _("\nThe state file level has changed. You must redo\n" "the fill command.\n")); + exit_code = 1; return; } } else { berrno be; Pmsg2(-1, _("\nCould not find the state file: %s ERR=%s\n" "You must redo the fill command.\n"), buf, be.bstrerror()); + exit_code = 1; return; } - do_unfill(); + if (!do_unfill()) { + exit_code = 1; + } this_block = NULL; } -static void do_unfill() +/* + * This is the second part of the fill command. After the tape or + * tapes are written, we are called here to reread parts, particularly + * the last block. + */ +static bool do_unfill() { DEV_BLOCK *block = dcr->block; int autochanger; + bool rc = false; dumped = 0; VolBytes = 0; LastBlock = 0; - Dmsg0(20, "Enter do_unfill\n"); + Pmsg0(000, "Enter do_unfill\n"); dev->set_cap(CAP_ANONVOLS); /* allow reading any volume */ dev->clear_cap(CAP_LABEL); /* don't label anything here */ @@ -2172,8 +2511,10 @@ static void do_unfill() } autochanger = autoload_device(dcr, 1, NULL); if (autochanger != 1) { + Pmsg1(100, "Autochanger returned: %d\n", autochanger); dev->close(); get_cmd(_("Mount first tape. Press enter when ready: ")); + Pmsg0(000, "\n"); } } @@ -2211,6 +2552,7 @@ static void do_unfill() if (compare_blocks(last_block, block)) { if (simple) { Pmsg0(-1, _("\nThe last block on the tape matches. Test succeeded.\n\n")); + rc = true; } else { Pmsg0(-1, _("\nThe last block of the first tape matches.\n\n")); } @@ -2234,8 +2576,10 @@ static void do_unfill() autochanger = autoload_device(dcr, 1, NULL); if (autochanger != 1) { + Pmsg1(100, "Autochanger returned: %d\n", autochanger); dev->close(); get_cmd(_("Mount second tape. Press enter when ready: ")); + Pmsg0(000, "\n"); } dev->clear_read(); @@ -2275,12 +2619,14 @@ static void do_unfill() } if (compare_blocks(last_block, block)) { Pmsg0(-1, _("\nThe last block on the second tape matches. Test succeeded.\n\n")); + rc = true; } bail_out: free_block(last_block1); free_block(last_block2); free_block(first_block); + return rc; } /* Read 10000 records then stop */ @@ -2329,10 +2675,6 @@ static bool compare_blocks(DEV_BLOCK *last_block, DEV_BLOCK *block) return true; } - - - - /* * Write current block to tape regardless of whether or * not it is full. If the tape fills, attempt to @@ -2340,7 +2682,8 @@ static bool compare_blocks(DEV_BLOCK *last_block, DEV_BLOCK *block) */ static int flush_block(DEV_BLOCK *block, int dump) { - char ec1[50]; + char ec1[50], ec2[50]; + uint64_t rate; DEV_BLOCK *tblock; uint32_t this_file, this_block_num; @@ -2386,11 +2729,12 @@ static int flush_block(DEV_BLOCK *block, int dump) if (now <= 0) { now = 1; /* don't divide by zero */ } - kbs = (double)dev->VolCatInfo.VolCatBytes / (1000 * now); + rate = dev->VolCatInfo.VolCatBytes / now; vol_size = dev->VolCatInfo.VolCatBytes; - Pmsg4(000, _("End of tape %d:%d. VolumeCapacity=%s. Write rate = %.1f KB/s\n"), + Pmsg4(000, _("End of tape %d:%d. Volume Bytes=%s. Write rate = %sB/s\n"), dev->file, dev->block_num, - edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), kbs); + edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), + edit_uint64_with_suffix(rate, ec2)); if (simple) { stop = -1; /* stop, but do simplified test */ @@ -2455,6 +2799,8 @@ static void qfillcmd() memset(rec->data, i & 0xFF, i); rec->data_len = i; rewindcmd(); + init_speed(); + Pmsg1(0, _("Begin writing %d Bacula blocks to tape ...\n"), count); for (i=0; i < count; i++) { if (i % 100 == 0) { @@ -2471,6 +2817,7 @@ static void qfillcmd() } } printf("\n"); + print_speed(dev->VolCatInfo.VolCatBytes); weofcmd(); if (dev->has_cap(CAP_TWOEOF)) { weofcmd(); @@ -2489,23 +2836,13 @@ static void rawfill_cmd() { DEV_BLOCK *block = dcr->block; int stat; - int fd; uint32_t block_num = 0; uint32_t *p; int my_errno; - uint32_t i; - fd = open("/dev/urandom", O_RDONLY); - if (fd) { - read(fd, block->buf, block->buf_len); - close(fd); - } else { - uint32_t *p = (uint32_t *)block->buf; - srandom(time(NULL)); - for (i=0; ibuf_len/sizeof(uint32_t); i++) { - p[i] = random(); - } - } + fill_buffer(FILL_RANDOM, block->buf, block->buf_len); + init_speed(); + p = (uint32_t *)block->buf; Pmsg1(0, _("Begin writing raw blocks of %u bytes.\n"), block->buf_len); for ( ;; ) { @@ -2516,10 +2853,10 @@ static void rawfill_cmd() printf("+"); fflush(stdout); } - p[0] += p[13]; - for (i=1; i<(block->buf_len-sizeof(uint32_t))/sizeof(uint32_t)-1; i++) { - p[i] += p[i-1]; - } + + mix_buffer(FILL_RANDOM, block->buf, block->buf_len); + + jcr->JobBytes += stat; continue; } break; @@ -2529,6 +2866,8 @@ static void rawfill_cmd() berrno be; printf(_("Write failed at block %u. stat=%d ERR=%s\n"), block_num, stat, be.bstrerror(my_errno)); + + print_speed(jcr->JobBytes); weofcmd(); } @@ -2557,6 +2896,7 @@ static struct cmdstruct commands[] = { {NT_("rewind"), rewindcmd, _("rewind the tape")}, {NT_("scan"), scancmd, _("read() tape block by block to EOT and report")}, {NT_("scanblocks"),scan_blocks, _("Bacula read block by block to EOT and report")}, + {NT_("speed"), speed_test, _("[file_size=n(GB)|nb_file=3|skip_zero|skip_random|skip_raw|skip_block] report drive speed")}, {NT_("status"), statcmd, _("print tape status")}, {NT_("test"), testcmd, _("General test Bacula tape functions")}, {NT_("weof"), weofcmd, _("write an EOF on the tape")}, @@ -2666,7 +3006,7 @@ bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing) { Dmsg0(20, "Enter dir_get_volume_info\n"); - bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName)); + dcr->setVolCatName(dcr->VolumeName); return 1; } @@ -2719,10 +3059,12 @@ bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) } autochanger = autoload_device(dcr, 1, NULL); if (autochanger != 1) { + Pmsg1(100, "Autochanger returned: %d\n", autochanger); fprintf(stderr, _("Mount blank Volume on device %s and press return when ready: "), dev->print_name()); dev->close(); getchar(); + Pmsg0(000, "\n"); } labelcmd(); VolumeName = NULL; @@ -2732,7 +3074,8 @@ bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) static bool my_mount_next_read_volume(DCR *dcr) { - char ec1[50]; + char ec1[50], ec2[50]; + uint64_t rate; JCR *jcr = dcr->jcr; DEV_BLOCK *block = dcr->block; @@ -2750,9 +3093,10 @@ static bool my_mount_next_read_volume(DCR *dcr) if (now <= 0) { now = 1; } - kbs = (double)VolBytes / (1000.0 * (double)now); - Pmsg3(-1, _("Read block=%u, VolBytes=%s rate=%.1f KB/s\n"), block->BlockNumber, - edit_uint64_with_commas(VolBytes, ec1), (float)kbs); + rate = VolBytes / now; + Pmsg3(-1, _("Read block=%u, VolBytes=%s rate=%sB/s\n"), block->BlockNumber, + edit_uint64_with_commas(VolBytes, ec1), + edit_uint64_with_suffix(rate, ec2)); if (strcmp(dcr->VolumeName, "TestVolume2") == 0) { end_of_tape = 1; @@ -2774,8 +3118,8 @@ static void set_volume_name(const char *VolName, int volnum) DCR *dcr = jcr->dcr; VolumeName = VolName; vol_num = volnum; - bstrncpy(dev->VolCatInfo.VolCatName, VolName, sizeof(dev->VolCatInfo.VolCatName)); - bstrncpy(dcr->VolCatInfo.VolCatName, VolName, sizeof(dcr->VolCatInfo.VolCatName)); + dev->setVolCatName(VolName); + dcr->setVolCatName(VolName); bstrncpy(dcr->VolumeName, VolName, sizeof(dcr->VolumeName)); dcr->VolCatInfo.Slot = volnum; dcr->VolCatInfo.InChanger = true;