diff --git a/linux/file.c b/linux/file.c index 236075b8629..def078020e4 100644 --- a/linux/file.c +++ b/linux/file.c @@ -192,10 +192,10 @@ toku_os_full_pwrite (int fd, const void *buf, size_t len, toku_off_t off) { assert(len == 0); } -int +ssize_t toku_os_pwrite (int fd, const void *buf, size_t len, toku_off_t off) { const char *bp = (const char *) buf; - int result = 0; + ssize_t result = 0; while (len > 0) { ssize_t r; if (t_pwrite) { diff --git a/src/tests/loader-cleanup-test.c b/src/tests/loader-cleanup-test.c index a9bfffbfb52..7321b6b8585 100644 --- a/src/tests/loader-cleanup-test.c +++ b/src/tests/loader-cleanup-test.c @@ -52,7 +52,8 @@ enum test_type {commit, // close loader, commit txn abort_loader, // abort loader, abort txn abort_via_poll, // close loader, but poll function returns non-zero, abort txn enospc_w, // close loader, but close fails due to enospc return from toku_os_write - enospc_f}; // either loader->put() or loader->close() fails due to enospc return from do_fwrite() + enospc_f, // either loader->put() or loader->close() fails due to enospc return from do_fwrite() + enospc_p}; // loader->close() fails due to enospc return from toku_os_pwrite() int abort_on_poll = 0; // set when test_loader() called with test_type of abort_via_poll @@ -76,10 +77,12 @@ static int count_temp(char * dirname); static void get_inames(DBT* inames, DB** dbs); static int verify_file(char * dirname, char * filename); static void assert_inames_missing(DBT* inames); -static ssize_t bad_write(int, const void *, size_t); static void run_all_tests(void); static void free_inames(DBT* inames); + +#define NUM_ENOSPC_TYPES 3 + int fwrite_count = 0; int fwrite_enospc = 0; int fwrite_count_nominal = 0; // number of fwrite calls for normal operation, initially zero @@ -90,6 +93,29 @@ int write_enospc = 0; int write_count_nominal = 0; // number of write calls for normal operation, initially zero int write_count_trigger = 0; // sequence number of write call that will fail (zero disables induced failure) +int pwrite_count = 0; +int pwrite_enospc = 0; +int pwrite_count_nominal = 0; // number of pwrite calls for normal operation, initially zero +int pwrite_count_trigger = 0; // sequence number of pwrite call that will fail (zero disables induced failure) + +const char * fwrite_str = "fwrite"; +const char * write_str = "write"; +const char * pwrite_str = "pwrite"; + +static const char * +enospc_type_str (enum test_type t){ + const char * rval; + if (t == enospc_f) + rval = fwrite_str; + else if (t == enospc_w) + rval = write_str; + else if (t == enospc_p) + rval = pwrite_str; + else + assert(0); + return rval; +} + static size_t bad_fwrite (const void *ptr, size_t size, size_t nmemb, FILE *stream) { fwrite_count++; size_t r; @@ -107,7 +133,7 @@ static size_t bad_fwrite (const void *ptr, size_t size, size_t nmemb, FILE *stre } -ssize_t +static ssize_t bad_write(int fd, const void * bp, size_t len) { ssize_t r; write_count++; @@ -121,10 +147,24 @@ bad_write(int fd, const void * bp, size_t len) { return r; } +static ssize_t +bad_pwrite (int fd, const void *buf, size_t len, toku_off_t off) { + int r; + pwrite_count++; + if (pwrite_count_trigger == pwrite_count) { + pwrite_enospc++; + errno = ENOSPC; + r = -1; + } else { + r = pwrite(fd, buf, len, off); + } + return r; +} + // return number of temp files -int +static int count_temp(char * dirname) { int n = 0; @@ -143,7 +183,7 @@ count_temp(char * dirname) { // return non-zero if file exists -int +static int verify_file(char * dirname, char * filename) { int n = 0; DIR * dir = opendir(dirname); @@ -158,7 +198,7 @@ verify_file(char * dirname, char * filename) { return n; } -void +static void get_inames(DBT* inames, DB** dbs) { int i; for (i = 0; i < NUM_DBS; i++) { @@ -175,7 +215,7 @@ get_inames(DBT* inames, DB** dbs) { } -void +static void assert_inames_missing(DBT* inames) { int i; char * dir = env->i->real_data_dir; @@ -199,8 +239,7 @@ void free_inames(DBT* inames) { } #if 0 -void print_inames(DB** dbs); -void +static void print_inames(DB** dbs) { int i; for (i = 0; i < NUM_DBS; i++) { @@ -449,7 +488,7 @@ static void test_loader(enum test_type t, DB **dbs) dbt_init(&key, &k, sizeof(unsigned int)); dbt_init(&val, &v, sizeof(unsigned int)); r = loader->put(loader, &key, &val); - if (t == enospc_f || t == enospc_w) + if (t == enospc_f || t == enospc_w || t == enospc_p) failed_put = r; else CKERR(r); @@ -482,9 +521,10 @@ static void test_loader(enum test_type t, DB **dbs) r = loader->close(loader); assert(r); // not defined what close() returns when poll function returns non-zero } - else if ((t == enospc_f || t == enospc_w) + else if ((t == enospc_f || t == enospc_w || t == enospc_p) && !failed_put) { - printf("closing, but expecting failure from enospc\n"); + const char * type = enospc_type_str(t); + printf("closing, but expecting failure from enospc %s\n", type); r = loader->close(loader); if (!USE_PUTS) assert(r); @@ -505,8 +545,10 @@ static void test_loader(enum test_type t, DB **dbs) if (t == commit) { fwrite_count_nominal = fwrite_count; // capture how many fwrites were required for normal operation - write_count_nominal = write_count; // capture how many writes were required for normal operation - if (verbose) printf("Calls to fwrite nominal: %d, calls to write nominal: %d\n", fwrite_count_nominal, write_count_nominal); + write_count_nominal = write_count; // capture how many writes were required for normal operation + pwrite_count_nominal = pwrite_count; // capture how many pwrites were required for normal operation + if (verbose) printf("Calls to fwrite nominal: %d, calls to write nominal: %d, calls to pwrite nominal: %d\n", + fwrite_count_nominal, write_count_nominal, pwrite_count_nominal); r = txn->commit(txn, 0); CKERR(r); if (!USE_PUTS) { @@ -566,22 +608,24 @@ static void run_test(enum test_type t, int trigger) generate_permute_tables(); - write_count_trigger = 0; - fwrite_count_trigger = 0; - fwrite_count = fwrite_enospc = 0; - write_count = write_enospc = 0; + fwrite_count_trigger = fwrite_count = fwrite_enospc = 0; + write_count_trigger = write_count = write_enospc = 0; + pwrite_count_trigger = pwrite_count = pwrite_enospc = 0; + if (t == enospc_f) { fwrite_count_trigger = trigger; } else if (t == enospc_w) { write_count_trigger = trigger; } + else if (t == enospc_p) { + pwrite_count_trigger = trigger; + } db_env_set_func_loader_fwrite(bad_fwrite); db_env_set_func_write(bad_write); + db_env_set_func_pwrite(bad_pwrite); - if (t == enospc_w && trigger == 1) - printf("Inducing enospc on first call to toku_os_write()\n"); test_loader(t, dbs); for(int i=0;i 0); - if (verbose) printf("\n\nTesting loader with enospc induced at %s count %d\n", write_type[j], trigger); - run_test(t[j], trigger); + if (nominal > limit) { // if we didn't already test every possible case + // induce write error sprinkled through process + for (i = 2; i < 5; i++) { + trigger = nominal / i; + if (verbose) printf("\n\nTesting loader with enospc induced at %s count %d (of %d)\n", write_type, trigger, nominal); + run_test(t, trigger); + } + // induce write error at end of process + for (i = 0; i < limit; i++) { + trigger = nominal - i; + assert(trigger > 0); + if (verbose) printf("\n\nTesting loader with enospc induced at %s count %d (of %d)\n", write_type, trigger, nominal); + run_test(t, trigger); + } } } } diff --git a/toku_include/toku_portability.h b/toku_include/toku_portability.h index 4d5169d59fe..749bf8b7320 100644 --- a/toku_include/toku_portability.h +++ b/toku_include/toku_portability.h @@ -153,7 +153,7 @@ void toku_os_full_pwrite (int fd, const void *buf, size_t len, toku_off_t off) _ void toku_os_full_write (int fd, const void *buf, size_t len) __attribute__((__visibility__("default"))); // os_write returns 0 on success, otherwise an errno. -int toku_os_pwrite (int fd, const void *buf, size_t len, toku_off_t off) __attribute__((__visibility__("default"))); +ssize_t toku_os_pwrite (int fd, const void *buf, size_t len, toku_off_t off) __attribute__((__visibility__("default"))); int toku_os_write (int fd, const void *buf, size_t len) __attribute__((__visibility__("default"))); // wrapper around fsync