From efbbd7bac0d6ac283e8cae896aa99c75bae1bea5 Mon Sep 17 00:00:00 2001 From: Ken Pettit Date: Sun, 22 Nov 2015 17:22:05 -0600 Subject: [PATCH] examples/smart-test: Adds a circular log type test for SMARTFS wear level code testing. From Ken Petit --- examples/smart_test/smart_test.c | 323 ++++++++++++++++++++++++++++--- 1 file changed, 297 insertions(+), 26 deletions(-) diff --git a/examples/smart_test/smart_test.c b/examples/smart_test/smart_test.c index b3bf4865a..b343e50bf 100644 --- a/examples/smart_test/smart_test.c +++ b/examples/smart_test/smart_test.c @@ -58,9 +58,14 @@ static int *g_linePos; static int *g_lineLen; +static int g_seekCount = 0; +static int g_writeCount = 0; +static int g_circCount = 0; + static int g_lineCount = 2000; -static int g_seekCount = 2000; -static int g_writeCount = 2000; +static int g_recordLen = 64; +static int g_eraseCount = 32; +static int g_totalRecords = 40000; /**************************************************************************** * Private Functions @@ -344,6 +349,239 @@ static int smart_seek_with_write_test(char *filename) return OK; } +/**************************************************************************** + * Name: smart_circular_log_test + * + * Description: Conducts a record based circular log seek / update test + * + * NOTE!!! + * In the test below, we use open / write, not fopen / fwrite. If the + * fopen / fwrite routines were used instead, they would provide us with + * stream buffer management which we DO NOT WANT. This will throw off + * the SMARTFS write sizes which will not generate the optimized wear + * operations. + * + ****************************************************************************/ + +static int smart_circular_log_test(char *filename) +{ + int fd; + char *buffer; + char *cmpbuf; + int s1; + int x; + int recordNo; + int bufSize; + int pass = TRUE; + + /* Calculate the size of our granular "erase record" writes */ + + bufSize = g_recordLen * g_eraseCount; + if (bufSize == 0) + { + printf("Invalid record parameters\n"); + return -EINVAL; + } + + /* Allocate memory for the record */ + + buffer = malloc(bufSize); + if (buffer == NULL) + { + printf("Unable to allocate memory for record storage\n"); + return -ENOMEM; + } + + /* Allocate memory for the compare buffer */ + + cmpbuf = malloc(g_recordLen); + if (cmpbuf == NULL) + { + printf("Unable to allocate memory for record storage\n"); + free(buffer); + return -ENOMEM; + } + + /* Open the circular log file */ + + fd = open(filename, O_RDWR | O_CREAT); + if (fd == -1) + { + printf("Unable to create file %s\n", filename); + free(buffer); + free(cmpbuf); + return -ENOENT; + } + + /* Now fill the circular log with dummy 0xFF data */ + + printf("Creating circular log with %d records\n", g_totalRecords); + memset(buffer, 0xFF, g_recordLen); + for (x = 0; x < g_totalRecords; x++) + { + write(fd, buffer, g_recordLen); + } + + close(fd); + + /* Now reopen the file for read/write mode */ + + fd = open(filename, O_RDWR); + if (fd == -1) + { + printf("Unable to open file %s\n", filename); + free(buffer); + free(cmpbuf); + return -ENOENT; + } + + printf("Performing %d circular log record update tests\n", + g_circCount); + + /* Start at record number zero and start updating log entries */ + + recordNo = 0; + for (x = 0; x < g_circCount; x++) + { + /* Fill a new record with random data */ + + for (s1=0; s1 < g_recordLen; s1++) + { + buffer[s1] = rand() & 0xFF; + } + + /* Set the first byte of the record (flag byte) to 0xFF */ + + buffer[0] = 0xFF; + + /* Seek to the record location in the file */ + + lseek(fd, g_recordLen*recordNo, SEEK_SET); + + /* Write the new record to the file */ + + if ((recordNo & (g_eraseCount-1)) == 0) + { + /* Every g_eraseCount records we will write a larger + * buffer with our record and padded with 0xFF to + * the end of our larger buffer. + */ + + memset(&buffer[g_recordLen], 0xFF, bufSize-g_recordLen); + write(fd, buffer, bufSize); + } + else + { + /* Just write a single record */ + + write(fd, buffer, g_recordLen); + } + + /* Now perform a couple of simulated flag updates */ + + lseek(fd, g_recordLen*recordNo, SEEK_SET); + buffer[0] = 0xFE; + write(fd, buffer, 1); + lseek(fd, g_recordLen*recordNo, SEEK_SET); + buffer[0] = 0xFC; + write(fd, buffer, 1); + + /* Now read the data back and compare it */ + + lseek(fd, g_recordLen*recordNo, SEEK_SET); + read(fd, cmpbuf, g_recordLen); + + for (s1 = 0; s1 < g_recordLen; s1++) + { + if (buffer[s1] != cmpbuf[s1]) + { + printf("\nCompare failure in record %d, offset %d\n", + recordNo, recordNo*g_recordLen+s1); + printf("\tExpected \"%02x\"", cmpbuf[s1]); + printf("\rReceived \"%02x\"", buffer[s1]); + pass = FALSE; + break; + } + } + + printf("\r%d", x); + fflush(stdout); + + /* Increment to the next record */ + + if (++recordNo >= g_totalRecords) + { + recordNo = 0; + } + } + + if (pass) + { + printf("\nPass\n"); + } + else + { + printf("\nFail\n"); + } + + close(fd); + free(buffer); + free(cmpbuf); + return OK; +} + +/**************************************************************************** + * Name: smart_usage + * + * Description: Displays usage information for the command. + * + ****************************************************************************/ +static void smart_usage(void) +{ + fprintf(stderr, "usage: smart_test [-c COUNT] [-s SEEKCOUNT] [-w WRITECOUNT] smart_mounted_filename\n\n"); + + fprintf(stderr, "DESCRIPTION\n"); + fprintf(stderr, " Conducts various stress tests to validate SMARTFS operation.\n"); + fprintf(stderr, " Please choose one or more of -c, -s, or -w to conduct tests.\n\n"); + + fprintf(stderr, "OPTIONS\n"); + fprintf(stderr, " -c COUNT\n"); + fprintf(stderr, " Performs a circular log style test where a fixed number of fixed\n"); + fprintf(stderr, " length records are written and then overwritten with new data.\n"); + fprintf(stderr, " Uses the -r, -e and -t options to specify the parameters of the \n"); + fprintf(stderr, " record geometry and update operation. The COUNT parameter sets\n"); + fprintf(stderr, " the number of record updates to perform.\n\n"); + + fprintf(stderr, " -s SEEKCOUNT\n"); + fprintf(stderr, " Performs a simple seek test where to validate the SMARTFS seek\n"); + fprintf(stderr, " operation. Uses the -l option to specify the number of test\n"); + fprintf(stderr, " lines to write to the test file. The SEEKCOUNT parameter sets\n"); + fprintf(stderr, " the number of seek/read operations to perform.\n\n"); + + fprintf(stderr, " -w WRITECOUNT\n"); + fprintf(stderr, " Performs a seek/write/seek/read test where to validate the SMARTFS\n"); + fprintf(stderr, " seek/write operation. Uses the -l option to specifiy the number of\n"); + fprintf(stderr, " test lines to write to the test file. The WRITECOUNT parameter sets\n"); + fprintf(stderr, " the number of seek/write operations to perform.\n\n"); + + fprintf(stderr, " -l LINECOUNT\n"); + fprintf(stderr, " Sets the number of lines of test data to write to the test file\n"); + fprintf(stderr, " during seek and seek/write tests.\n\n"); + + fprintf(stderr, " -r RECORDLEN\n"); + fprintf(stderr, " Sets the length of each log record during circular log tests.\n\n"); + + fprintf(stderr, " -e ERASECOUNT\n"); + fprintf(stderr, " Sets the erase granularity for overwriting old circular log entries.\n"); + fprintf(stderr, " Setting this value to 16, for instance, would cause every 16th record\n"); + fprintf(stderr, " update to write a single record followed by 15 records with all 0xFF\n"); + fprintf(stderr, " content. This helps SMARTFS perform better wear leveling and reduces\n"); + fprintf(stderr, " the number of FLASH block erases significantly.\n\n"); + + fprintf(stderr, " -t TOTALRECORDS\n"); + fprintf(stderr, " Sets the total number of records in the circular log test file.\n\n"); +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -355,35 +593,50 @@ int smart_test_main(int argc, char *argv[]) #endif { int ret, opt; - int gTestCount = 10000; /* Argument given? */ - while ((opt = getopt(argc, argv, "l:s:w:")) != -1) + while ((opt = getopt(argc, argv, "c:e:l:r:s:t:w:")) != -1) { switch (opt) { + case 'c': + g_circCount = atoi(optarg); + break; + + case 'e': + g_eraseCount = atoi(optarg); + break; + case 'l': g_lineCount = atoi(optarg); break; + case 'r': + g_recordLen = atoi(optarg); + break; + case 's': g_seekCount = atoi(optarg); break; + case 't': + g_totalRecords = atoi(optarg); + break; + case 'w': g_writeCount = atoi(optarg); break; default: /* '?' */ - fprintf(stderr, "usage: smart_test [-l lineCount] [-s seekCount] [-w writeCount] smart_mounted_filename\n"); + smart_usage(); exit(EXIT_FAILURE); } } - if (argc < 2) + if (argc < 2 || (g_seekCount + g_writeCount + g_circCount == 0)) { - fprintf(stderr, "usage: smart_test [-l lineCount] [-s seekCount] [-w writeCount] smart_mounted_filename\n"); + smart_usage(); return -1; } @@ -402,30 +655,48 @@ int smart_test_main(int argc, char *argv[]) return -1; } - /* Create a test file */ + /* Test if performing seek test or write test */ - if ((ret = smart_create_test_file(argv[optind])) < 0) + if (g_seekCount > 0 || g_writeCount > 0) { - goto err_out_with_mem; + /* Create a test file */ + + if ((ret = smart_create_test_file(argv[optind])) < 0) + { + goto err_out_with_mem; + } + + /* Conduct a seek test? */ + + if (g_seekCount > 0 ) + { + if ((ret = smart_seek_test(argv[optind])) < 0) + { + goto err_out_with_mem; + } + } + + /* Conduct an append test */ + + if ((ret = smart_append_test(argv[optind])) < 0) + { + goto err_out_with_mem; + } + + /* Conduct a seek with write test? */ + + if (g_writeCount > 0) + { + if ((ret = smart_seek_with_write_test(argv[optind])) < 0) + { + goto err_out_with_mem; + } + } } - /* Conduct a seek test */ + /* Perform a "circular log" test */ - if ((ret = smart_seek_test(argv[optind])) < 0) - { - goto err_out_with_mem; - } - - /* Conduct an append test */ - - if ((ret = smart_append_test(argv[optind])) < 0) - { - goto err_out_with_mem; - } - - /* Conduct a seek with write test */ - - if ((ret = smart_seek_with_write_test(argv[optind])) < 0) + if ((ret = smart_circular_log_test(argv[optind])) < 0) { goto err_out_with_mem; }