diff --git a/src/osd/osd_rmw.cpp b/src/osd/osd_rmw.cpp index 651b07b2..c34a0d76 100644 --- a/src/osd/osd_rmw.cpp +++ b/src/osd/osd_rmw.cpp @@ -286,6 +286,10 @@ static void* get_jerasure_decoding_matrix(osd_rmw_stripe_t *stripes, int pg_size if (edd == 0) return NULL; reed_sol_matrix_t *matrix = get_ec_matrix(pg_size, pg_minsize); +#ifdef WITH_ISAL + if (item_size) + *item_size = matrix->isal_item_size; +#endif auto dec_it = matrix->decodings.find((reed_sol_erased_t){ .data = erased, .size = pg_size }); if (dec_it == matrix->decodings.end()) { @@ -330,7 +334,6 @@ static void* get_jerasure_decoding_matrix(osd_rmw_stripe_t *stripes, int pg_size int *erased_copy = (int*)(rectable + 32*smrow*pg_minsize); memcpy(erased_copy, erased, pg_size*sizeof(int)); matrix->decodings.emplace((reed_sol_erased_t){ .data = erased_copy, .size = pg_size }, rectable); - *item_size = matrix->isal_item_size; return rectable; #else int *dm_ids = (int*)malloc_or_die(sizeof(int)*(pg_minsize + pg_minsize*pg_minsize + pg_size)); diff --git a/src/osd/osd_rmw_test.cpp b/src/osd/osd_rmw_test.cpp index 914f0207..f3eaa5af 100644 --- a/src/osd/osd_rmw_test.cpp +++ b/src/osd/osd_rmw_test.cpp @@ -28,6 +28,7 @@ void test14(); void test15(bool second); void test16(); void test_recover_22_d2(); +void test_recover_22_d01_cache(); void test_ec43_error_bruteforce(); void test_recover_53_d5(); void test_recover_22(); @@ -68,6 +69,8 @@ int main(int narg, char *args[]) test16(); // Test 17 test_recover_22_d2(); + // Test 17b: regression — cached ISA-L decoder with multi-range missing data roles + test_recover_22_d01_cache(); // Error bruteforce test_ec43_error_bruteforce(); test_ec_find_good_multi_chunks(); @@ -1120,6 +1123,77 @@ void test_recover_22_d2() /*** +17b. EC 2+2 — two missing data roles with different read ranges, decoder cached. + Regression test for the get_jerasure_decoding_matrix() / reconstruct_stripes_ec() + bug where *item_size was set only on cache miss, so on a subsequent call + (cache hit) item_size stayed 0 and `dectable + wanted_base*item_size*pg_minsize` + collapsed to dectable for every group — the second `recover_seq()` invocation + then decoded role 1 using the matrix row that reconstructs role 0. + +***/ + +void test_recover_22_d01_cache() +{ + use_ec(4, 2, true); + // Step 1: encode known data through the normal path to obtain the two parity blocks. + osd_num_t enc_set[4] = { 1, 2, 3, 4 }; + osd_rmw_stripe_t enc[4] = {}; + split_stripes(2, 8192, 0, 16384, enc); + void *write_buf = malloc_or_die(16384); + set_pattern((uint8_t*)write_buf+0*4096, 4096, PATTERN0); // data 0, [0..4K) + set_pattern((uint8_t*)write_buf+1*4096, 4096, PATTERN1); // data 0, [4K..8K) + set_pattern((uint8_t*)write_buf+2*4096, 4096, PATTERN2); // data 1, [0..4K) + set_pattern((uint8_t*)write_buf+3*4096, 4096, PATTERN3); // data 1, [4K..8K) + void *rmw_buf = calc_rmw(write_buf, enc, enc_set, 4, 2, 4, enc_set, 8192, 0); + calc_rmw_parity_ec(enc, 4, 2, enc_set, enc_set, 8192, 0); + // Step 2: first reconstruction — cache miss, same read range for both missing roles. + // Always passes; this populates matrix->decodings for the (1,1,0,0) erasure pattern. + { + osd_rmw_stripe_t s[4] = {}; + uint8_t *buf = (uint8_t*)malloc_or_die(8192*4); + for (int i = 0; i < 4; i++) + { + s[i].read_start = 0; + s[i].read_end = 8192; + s[i].read_buf = buf + i*8192; + } + s[0].missing = true; + s[1].missing = true; + memcpy(s[2].read_buf, enc[2].write_buf, 8192); + memcpy(s[3].read_buf, enc[3].write_buf, 8192); + reconstruct_stripes_ec(s, 4, 2, 0); + check_pattern((uint8_t*)s[0].read_buf+0*4096, 4096, PATTERN0); + check_pattern((uint8_t*)s[0].read_buf+1*4096, 4096, PATTERN1); + check_pattern((uint8_t*)s[1].read_buf+0*4096, 4096, PATTERN2); + check_pattern((uint8_t*)s[1].read_buf+1*4096, 4096, PATTERN3); + free(buf); + } + // Step 3: second reconstruction — cache HIT, missing roles have DIFFERENT ranges, + // so recover_seq() runs twice and wanted_base becomes 1 between the calls. + // With the bug, role 1 gets decoded using row 0 of the rectable (PATTERN1 instead of PATTERN3). + { + osd_rmw_stripe_t s[4] = {}; + uint8_t *buf = (uint8_t*)malloc_or_die(8192*4); + for (int i = 0; i < 4; i++) + s[i].read_buf = buf + i*8192; + s[0].read_start = 0; s[0].read_end = 4096; s[0].missing = true; + s[1].read_start = 4096; s[1].read_end = 8192; s[1].missing = true; + s[2].read_start = 0; s[2].read_end = 8192; + s[3].read_start = 0; s[3].read_end = 8192; + memcpy(s[2].read_buf, enc[2].write_buf, 8192); + memcpy(s[3].read_buf, enc[3].write_buf, 8192); + reconstruct_stripes_ec(s, 4, 2, 0); + check_pattern((uint8_t*)s[0].read_buf, 4096, PATTERN0); + check_pattern((uint8_t*)s[1].read_buf, 4096, PATTERN3); + free(buf); + } + use_ec(4, 2, false); + free(rmw_buf); + free(write_buf); +} + +/*** + 18. EC 4+2 error location bruteforce ***/