From 48c05f9da102f13e72aa882601579319b9248549 Mon Sep 17 00:00:00 2001 From: aitassou Date: Wed, 3 Sep 2025 09:18:27 +0200 Subject: [PATCH] feature: LRC (locally repairable code) backend This is similar to the isa_l_rs_vand_inv backend, but takes an additional parameter l, the number of local parities. The first g := m - l parities are "global" parities and computed exactly the same as for isa_l_rs_vand_inv. The last l parities are "local" parities, whose coefficients may be combined into one additional global parity. As a result, decoding will succeed for all possible sets of k + l - 1 fragments. Each local parity is grouped with a set of data fragments such that any one of them may be reconstructed from the others, rather than requiring a full k fragments. For example, given a scheme like k = 8, m = 4, l = 2, then * fragments 0 through 7 are data fragments * fragments 8 and 9 are global parities * fragments 10 and 11 are local parities * any set of 9 unique fragments will be able to decode the original data * any set of 4 unique fragments from 0, 1, 2, 3, and 10 will be able to reconstruct the missing fragment and similarly * any set of 4 unique fragments from 4, 5, 6, 7, and 11 will be able to reconstruct the missing fragment. If k is not evenly divisible by l, groups are sized to be within one fragment of each other, with the larger groups having earlier data fragments. For example, given a scheme like k = 15, m = 5, l = 4, then the local reconstruction groups are * fragments {0, 1, 2, 3, 16} * fragments {4, 5, 6, 7, 17} * fragments {8, 9, 10, 11, 18} * fragments {12, 13, 14, 19} Co-Authored-By: Tim Burke Signed-off-by: Tim Burke Change-Id: I2884cda24ba72d4025b175f4357ccd7ffbb48c63 --- doc/erasure_coding.md | 25 +- include/erasurecode/erasurecode.h | 4 + include/isa_l/isa_l_common.h | 65 ++ src/Makefile.am | 1 + src/backends/isa-l/isa_l_common.c | 25 - src/backends/isa-l/isa_l_rs_lrc.c | 872 ++++++++++++++++++++++++++ src/erasurecode.c | 2 + test/liberasure_rs_isal_stress_test.c | 164 ++++- test/liberasurecode_test.c | 159 ++++- 9 files changed, 1269 insertions(+), 48 deletions(-) create mode 100644 src/backends/isa-l/isa_l_rs_lrc.c diff --git a/doc/erasure_coding.md b/doc/erasure_coding.md index 9b7cbc1..1b52395 100644 --- a/doc/erasure_coding.md +++ b/doc/erasure_coding.md @@ -106,6 +106,22 @@ Provided by isa-l submatrix, then calculate `E = inv(V′) × V`. This makes a systematic code that is optimal for all `k` and `m`. +- `isa_l_rs_lrc` (added in liberasurecode 1.8.0) + + Uses the Reed-Solomon functions provided by isa-l with an encoding matrix + provided by liberasurecode. Similar to `isa_l_rs_vand_inv`, but adds a new + parameter `l` for the number of "local parities". To construct the encoding + matrix, start with an `isa_l_rs_vand_inv` encoding matrix. The first `m - l` + parity lines are left as "global parities". + The last `l` lines are all crafted from the "next" global parity, this is + done in order to combine them and get one global parity and ensure the + original data may be decoded from any `k + l - 1` unique fragments. + The last `l` lines are each assigned a group of `⌊k / l⌋` or `⌈k / l⌉` data + fragments; within each line, the columns corresponding to those data + fragments are retained and all others are set to zero. This reduces the + minimum number of fragments required to reconstruct data or local parity + fragments, provided the other fragments in the group are available. + - `isa_l_rs_cauchy` (added in liberasurecode 1.4.0, pyeclib 1.4.0) Uses the Reed-Solomon functions provided by isa-l with @@ -125,8 +141,8 @@ Proprietary Classifications =============== -Required Fragments ------------------- +Required Fragments to Decode +---------------------------- ### n ≡ k Most supported backends are optimal erasure codes, where any `k` fragments @@ -138,6 +154,11 @@ The flat XOR codes require more than `k` fragments to decode in the general case. In particular, `flat_xor_hd3` requires at least `n ≡ k + m - 2` fragments and `flat_xor_hd4` requires at least `n ≡ k + m - 3`. +LRC codes are intentionally not optimal, as each local parity is only useful +in recovering the original data if there is at least one data fragment missing +in the same local group. However, decoding will always succeed with +`n ≡ k + l - 1` unique fragments. + Systematic vs. Non-systematic ----------------------------- diff --git a/include/erasurecode/erasurecode.h b/include/erasurecode/erasurecode.h index bdb8562..6918873 100644 --- a/include/erasurecode/erasurecode.h +++ b/include/erasurecode/erasurecode.h @@ -51,6 +51,7 @@ typedef enum { EC_BACKEND_ISA_L_RS_CAUCHY = 7, EC_BACKEND_LIBPHAZR = 8, EC_BACKEND_ISA_L_RS_VAND_INV = 9, + EC_BACKEND_ISA_L_RS_LRC = 10, EC_BACKENDS_MAX, } ec_backend_id_t; @@ -80,6 +81,9 @@ struct ec_args { struct { uint64_t arg1; /* sample arg */ } null_args; /* args specific to the null codes */ + struct { + int l; /* number of local parities */ + } lrc_args; /* args specific to locally recoverable codes */ struct { uint64_t x, y; /* reserved for future expansion */ uint64_t z, a; /* reserved for future expansion */ diff --git a/include/isa_l/isa_l_common.h b/include/isa_l/isa_l_common.h index 30f18e6..5aac4b7 100644 --- a/include/isa_l/isa_l_common.h +++ b/include/isa_l/isa_l_common.h @@ -57,6 +57,7 @@ typedef struct { unsigned char *encode_tables; int k; int m; + int l; //local parities int w; } isa_l_descriptor; @@ -71,3 +72,67 @@ int isa_l_element_size(void* desc); int isa_l_exit(void *desc); void * isa_l_common_init(struct ec_backend_args *args, void *backend_sohandle, const char* gen_matrix_func_name); + +/* global helper functions */ +static inline int get_num_missing_elements(int *missing_idxs) +{ + int i = 0; + + while (missing_idxs[i] > -1) { + i++; + } + + return i; +} + +static inline void mult_and_xor_row(unsigned char *to_row, + unsigned char *from_row, + unsigned char val, + int num_elems, + gf_mul_func gf_mul) +{ + int i; + + for (i = 0; i < num_elems; i++) { + to_row[i] ^= gf_mul(val, from_row[i]); + } +} +/* LRC helper functions */ +static inline int local_group_size(int k, int l, int n) { + int extra = k % l; + if (n < extra) { + return (k / l) + 1; + } else { + return k / l; + } +} + +static inline int local_group_data_lower(int k, int l, int n) { + int extra = k % l; + int group_size = (k / l) + 1; + if (n < extra) { + return n * group_size; + } else { + return extra * group_size + (n - extra) * (group_size - 1); + } +} + +static inline int local_group_data_upper(int k, int l, int n) { + return local_group_data_lower(k, l, n + 1); +} + +static inline int local_group_for_data(int k, int l, int n) { + int extra = k % l; + /** + * Start off assuming larger groups; we'll adjust them + * smaller later if needed + */ + int group_size = (k / l) + 1; + if (n < extra * group_size) { + return n / group_size; + } else { + n -= extra * group_size; + group_size--; + return extra + n / group_size; + } +} diff --git a/src/Makefile.am b/src/Makefile.am index 3bcc3e1..6b4efb8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -27,6 +27,7 @@ liberasurecode_la_SOURCES = \ backends/isa-l/isa_l_common.c \ backends/isa-l/isa_l_rs_vand.c \ backends/isa-l/isa_l_rs_vand_inv.c \ + backends/isa-l/isa_l_rs_lrc.c \ backends/isa-l/isa_l_rs_cauchy.c \ backends/rs_vand/liberasurecode_rs_vand.c \ backends/shss/shss.c \ diff --git a/src/backends/isa-l/isa_l_common.c b/src/backends/isa-l/isa_l_common.c index 0e9eb5c..e86c87b 100644 --- a/src/backends/isa-l/isa_l_common.c +++ b/src/backends/isa-l/isa_l_common.c @@ -68,7 +68,6 @@ static unsigned char* isa_l_get_decode_matrix(int k, int m, unsigned char *encod } l++; } - if (i != k) { free(decode_matrix); decode_matrix = NULL; @@ -77,30 +76,6 @@ static unsigned char* isa_l_get_decode_matrix(int k, int m, unsigned char *encod return decode_matrix; } -static int get_num_missing_elements(int *missing_idxs) -{ - int i = 0; - - while (missing_idxs[i] > -1) { - i++; - } - - return i; -} - -static void mult_and_xor_row(unsigned char *to_row, - unsigned char *from_row, - unsigned char val, - int num_elems, - gf_mul_func gf_mul) -{ - int i; - - for (i = 0; i < num_elems; i++) { - to_row[i] ^= gf_mul(val, from_row[i]); - } -} - /* * TODO: Add in missing parity rows and adjust the inverse_rows to * be used for parity. diff --git a/src/backends/isa-l/isa_l_rs_lrc.c b/src/backends/isa-l/isa_l_rs_lrc.c new file mode 100644 index 0000000..bb3768d --- /dev/null +++ b/src/backends/isa-l/isa_l_rs_lrc.c @@ -0,0 +1,872 @@ +/* + * Copyright 2025 aitassou + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY + * THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * isa_l_rs_lrc backend implementation + * + * vi: set noai tw=79 ts=4 sw=4: + */ + +#include +#include "erasurecode_backend.h" +#include "erasurecode_helpers.h" +#include "isa_l_common.h" + +#define ISA_L_RS_LRC_LIB_MAJOR 1 +#define ISA_L_RS_LRC_LIB_MINOR 0 +#define ISA_L_RS_LRC_LIB_REV 0 +#define ISA_L_RS_LRC_LIB_VER_STR "1.0" +#define ISA_L_RS_LRC_LIB_NAME "isa_l_rs_vand" +#if defined(__MACOS__) || defined(__MACOSX__) || defined(__OSX__) || defined(__APPLE__) +#define ISA_L_RS_LRC_SO_NAME "libisal" LIBERASURECODE_SO_SUFFIX ".dylib" +#else +#define ISA_L_RS_LRC_SO_NAME "libisal" LIBERASURECODE_SO_SUFFIX ".so.2" +#endif + +/* Forward declarations */ +struct ec_backend_common backend_isa_l_rs_lrc; + +static int gen_encoding_matrix(isa_l_descriptor * desc, int m, int k) { + int i, j, ret = 1; + unsigned char p, gen = 2; + unsigned char *tmp = NULL; + unsigned char *tmp_inv_k = NULL; + int n = m + k; + int l = desc->l; //local parities + int r = m - l; //global parities + + /* Build a (k+m)*k Vandermonde matrix, A */ + tmp = malloc(sizeof(char) * n * k); + if (tmp == NULL) { + goto error_free; + } + for (i = 0; i < n; i++) { + p = 1; + for (j = 0; j < k; j++) { + tmp[k * i + j] = p; + p = desc->gf_mul(p, gen); + } + if (i < k + r) { + gen = desc->gf_mul(gen, 2); + } + } + + /* It starts with a k*k submatrix, A'; calculate inv(A') */ + tmp_inv_k = malloc(sizeof(char) * k * k); + if (tmp_inv_k == NULL) { + goto error_free; + } + int im_ret = desc->gf_invert_matrix(tmp, tmp_inv_k, k); + if (im_ret < 0) { + /** + * Should never happen as it's a proper Vandermonde matrix, + * but belt & bracers... + */ + goto error_free; + } + + /** + * Now we're ready to build the encoding matrix: inv(A') * A. + * Save some multiplies by going straight to I for the start. + */ + memset(desc->matrix, 0, k * n); + for (i = 0; i < k; i++) + desc->matrix[k * i + i] = 1; + + /* Then multiply inv(A') by the rest of A for the parities */ + for (i = k; i < n; i++) { + for (j = 0; j < k; j++) { + p = 0; + for (int u = 0; u < k; u++) { + p ^= desc->gf_mul(tmp[(i*k)+u], tmp_inv_k[(u*k)+j]); + } + desc->matrix[(i*k)+j] = p; + } + } + + int group_offset = 0; + for (i = 0; i < l; i++) { + int frag_num = k + r + i; + int group_size = local_group_size(k, l, i); + for (j = 0; j < k; j++) { + if ( j < group_offset || j >= group_offset + group_size) + desc->matrix[k * frag_num + j] = 0; + } + group_offset += group_size; + } + ret = 0; +error_free: + free(tmp_inv_k); + free(tmp); + + return ret; +} + +static void * isa_l_rs_lrc_init(struct ec_backend_args *args, + void *backend_sohandle) +{ + isa_l_descriptor *desc = NULL; + + desc = (isa_l_descriptor *)malloc(sizeof(isa_l_descriptor)); + if (NULL == desc) { + return NULL; + } + /* Set this early so we can have a single error path */ + desc->matrix = NULL; + + desc->k = args->uargs.k; + desc->m = args->uargs.m; + desc->l = args->uargs.priv_args1.lrc_args.l; + if (desc->l < 1 || desc->l > desc->m || (2 * desc->l > desc->k)) { + goto error; + } + if (args->uargs.w <= 0) + args->uargs.w = ISA_L_W; + desc->w = args->uargs.w; + + /* validate EC arguments */ + { + long long max_symbols = 1LL << desc->w; + if ((desc->k + desc->m) > max_symbols) { + goto error; + } + } + + /* + * ISO C forbids casting a void* to a function pointer. + * Since dlsym return returns a void*, we use this union to + * "transform" the void* to a function pointer. + */ + union { + ec_encode_data_func encodep; + ec_init_tables_func init_tablesp; + gf_gen_encoding_matrix_func gen_matrixp; + gf_invert_matrix_func invert_matrixp; + gf_mul_func gf_mulp; + void *vptr; + } func_handle = {.vptr = NULL}; + + /* fill in function addresses */ + func_handle.vptr = NULL; + func_handle.vptr = dlsym(backend_sohandle, "ec_encode_data"); + desc->ec_encode_data = func_handle.encodep; + if (NULL == desc->ec_encode_data) { + goto error; + } + + func_handle.vptr = NULL; + func_handle.vptr = dlsym(backend_sohandle, "ec_init_tables"); + desc->ec_init_tables = func_handle.init_tablesp; + if (NULL == desc->ec_init_tables) { + goto error; + } + + func_handle.vptr = NULL; + func_handle.vptr = dlsym(backend_sohandle, "gf_invert_matrix"); + desc->gf_invert_matrix = func_handle.invert_matrixp; + if (NULL == desc->gf_invert_matrix) { + goto error; + } + + func_handle.vptr = NULL; + func_handle.vptr = dlsym(backend_sohandle, "gf_mul"); + desc->gf_mul = func_handle.gf_mulp; + if (NULL == desc->gf_mul) { + goto error; + } + + desc->matrix = malloc(sizeof(char) * desc->k * (desc->k + desc->m)); + if (NULL == desc->matrix) { + goto error; + } + + if (0 != gen_encoding_matrix(desc, desc->m, desc->k)) { + goto error; + } + + /** + * Generate the tables for encoding + */ + desc->encode_tables = malloc(sizeof(unsigned char) * + (desc->k * desc->m * 32)); + if (NULL == desc->encode_tables) { + goto error; + } + + desc->ec_init_tables(desc->k, desc->m, + &desc->matrix[desc->k * desc->k], + desc->encode_tables); + + return desc; + +error: + free(desc->matrix); + free(desc); + + return NULL; +} + + +static int isa_l_rs_lrc_check_reconstruct_fragments(void *desc, int *missing_idxs, int destination_idx) { + isa_l_descriptor *d = (isa_l_descriptor*)desc; + uint64_t missing_bm = convert_list_to_bitmap(missing_idxs); + int missing_locals, local_group; + if (destination_idx < d->k) { + local_group = local_group_for_data(d->k, d->l, destination_idx); + int local_parity_index = d->k + d->m - d->l + local_group; + missing_locals = (1 << local_parity_index) & missing_bm; + for ( + int i = local_group_data_lower(d->k, d->l, local_group); + i < local_group_data_upper(d->k, d->l, local_group); + i++ + ) { + if (i != destination_idx) + missing_locals |= (1 << i) & missing_bm; + } + if (!missing_locals) { + return 0; + } + } else if (destination_idx >= d->k + d->m - d->l) { + local_group = destination_idx - d->k - d->m + d->l; + missing_locals = 0; + for ( + int i = local_group_data_lower(d->k, d->l, local_group); + i < local_group_data_upper(d->k, d->l, local_group); + i++ + ) { + missing_locals |= (1 << i) & missing_bm; + } + if (!missing_locals) { + return 0; + } + } + + // if we haven't returned yet, we can't do local-only reconstruction + int useful_frags = 0, can_use_local_parity = 0; + for (int i = 0; i < d->k + d->m; i++) { + if (i < d->k) { + if ((1 << i) & missing_bm) { + local_group = local_group_for_data(d->k, d->l, i); + can_use_local_parity |= 1 << (d->k + d->m - d->l + local_group); + } else { + useful_frags++; + } + } else if (i >= d->k + d->m - d->l) { + if ((1 << i) & can_use_local_parity && !((1 << i) & missing_bm)) { + useful_frags++; + } + } else { + if (!((1 << i) & missing_bm)) { + useful_frags++; + } + } + } + + if (useful_frags < d->k) { + return -EINSUFFFRAGS; + } + return 0; +} + + +/* + * For the time being, we only claim compatibility with versions that + * match exactly + */ +static bool isa_l_rs_lrc_is_compatible_with(uint32_t version) { + return version == backend_isa_l_rs_lrc.ec_backend_version; +} + +static unsigned char* get_lrc_inverse_rows(int k, + int m, + int min_range, + int max_range, + int missing_local_parity, + unsigned char *decode_inverse, + unsigned char* encode_matrix, + uint64_t missing_bm, + gf_mul_func gf_mul) +{ + int num_missing_elements = 0; + for (int i = 0; i < EC_MAX_FRAGMENTS; i++) + if ((1LLU << i) & missing_bm) + num_missing_elements++; + unsigned char *inverse_rows = (unsigned char*)malloc(sizeof(unsigned + char*) * k * num_missing_elements); + int i, j, l = 0; + + if (NULL == inverse_rows) { + return NULL; + } + + int matrix_size = max_range - min_range; + + int encode_matrix_size = (matrix_size == 0 ||matrix_size == k || missing_local_parity) ? k : matrix_size; + + int n = k + m; + + memset(inverse_rows, 0, sizeof(unsigned + char*) * matrix_size * num_missing_elements); + + /* + * Fill in rows for missing data + */ + for (i = 0; i < matrix_size; i++) { + if ((1 << i) & (missing_bm>>min_range)) { + for (j = 0; j < matrix_size; j++) { + inverse_rows[(l * matrix_size) + j] = decode_inverse[(i * matrix_size) + j]; + } + l++; + } + } + + /* + * Process missing parity. + * + * Start with an all-zero row. + * + * For each data element, if the data element is: + * + * Available: XOR the corresponding coefficient from the + * encoding matrix. + * + * Unavailable: multiply corresponding coefficient with + * the row that corresponds to the missing data in inverse_rows + * and XOR the resulting row with this row. + */ + for (i = k; i < n; i++) { + // Parity is missing + if ((1 << i) & (missing_bm)) { + int d_idx_avail = 0; + int d_idx_unavail = 0; + for (j = min_range; j < max_range; j++) { + // This data is available, so we can use the encode matrix + if (((1 << j) & (missing_bm)) == 0) { + inverse_rows[(l * matrix_size) + d_idx_avail] ^= encode_matrix[(i * encode_matrix_size) + j]; + d_idx_avail++; + } else { + mult_and_xor_row(&inverse_rows[l * matrix_size], + &inverse_rows[d_idx_unavail * matrix_size], + encode_matrix[(i * encode_matrix_size) + j], + k, + gf_mul); + d_idx_unavail++; + } + } + l++; + } + } + return inverse_rows; +} + +static unsigned char* isa_l_lrc_get_decode_matrix(int k, int m, unsigned local_parity, unsigned char *encode_matrix, uint64_t missing_bm, int *used_idxs, int *use_combined_parity) +{ + int i = 0, j, locate = 0; + int n = k + m; + int global_parity = m - local_parity; + int group_offset = 0; + uint64_t missing_local_parity = 0; + uint64_t use_parity = 0; + + int total_missing = 0; + + unsigned char *decode_matrix = malloc(sizeof(unsigned char) * k * k); + if( NULL == decode_matrix ) { + return NULL; + } + + for (int v = 0; v < local_parity; v++) { + int group_size = local_group_size(k, local_parity, v); + for (int u = group_offset; u < group_offset + group_size; u++) { + if ((1 << u) & missing_bm) { + use_parity |= (1 << (k + global_parity + v)); + } + } + group_offset += group_size; + + missing_local_parity |= (1 << (k + global_parity + v)) & missing_bm; + } + + for (locate = 0; i < k && locate < k + global_parity; locate++) { + if (((1 << locate) & missing_bm)) { + total_missing++; + continue; + } + for (j = 0; j < k; j++) { + decode_matrix[(k * i) + j] = encode_matrix[(k * locate) + j]; + } + used_idxs[locate] = 1; + i++; + } + // we can simplify here as total_missing counts only missing data + global parity + if (i < k && !missing_local_parity && (total_missing == global_parity + 1)) { + // Set flag to indicate we can use combined parity in case of + // g + 1 errors and no local parity is missing + *use_combined_parity = 1; + // We can combine all the local parities into a single global parity + group_offset = 0; + for (int v = 0; v < local_parity; v++) { + int group_size = local_group_size(k, local_parity, v); + for (int u = group_offset; u < group_offset + group_size; u++) { + decode_matrix[(i * k) + u] = encode_matrix[((locate + v) * k) + u]; + } + group_offset += group_size; + } + i++; + } + if (i < k) { + // Still not enough? Well, let's add what local parities we have, + // see if we can get lucky + for (locate = k + global_parity; i < k && locate < n; locate++) { + if (use_parity & (1 << locate) && !((1 << locate) & missing_bm)) { + for (j = 0; j < k; j++) { + decode_matrix[(k * i) + j] = encode_matrix[(k * locate) + j]; + } + used_idxs[locate] = 1; + i++; + } + } + } + if (i != k) { + free(decode_matrix); + decode_matrix = NULL; + } + return decode_matrix; +} + + +static int isa_l_lrc_decode(void *desc, char **data, char **parity, + int *missing_idxs, int blocksize) +{ + isa_l_descriptor *isa_l_desc = (isa_l_descriptor*)desc; + + unsigned char *g_tbls = NULL; + unsigned char *decode_matrix = NULL; + unsigned char *decode_inverse = NULL; + unsigned char *inverse_rows = NULL; + unsigned char **decoded_elements = NULL; + unsigned char **available_fragments = NULL; + int k = isa_l_desc->k; + int m = isa_l_desc->m; + int local_parity = isa_l_desc->l; + int n = k + m; + int ret = -1; + int i, j; + unsigned char *combined_local_parities = NULL; + int use_combined_parity = 0; + + int num_missing_elements = get_num_missing_elements(missing_idxs); + uint64_t missing_bm = convert_list_to_bitmap(missing_idxs); + + int *used_idxs = calloc(n, sizeof(int)); + if(NULL == used_idxs) { + goto out; + } + decode_matrix = isa_l_lrc_get_decode_matrix(k, m, local_parity, isa_l_desc->matrix, missing_bm, used_idxs, &use_combined_parity); + + if (NULL == decode_matrix) { + goto out; + } + + decode_inverse = (unsigned char*)malloc(sizeof(unsigned char) * k * k); + + if (NULL == decode_inverse) { + goto out; + } + + int im_ret = isa_l_desc->gf_invert_matrix(decode_matrix, decode_inverse, k); + if (im_ret < 0) { + goto out; + } + + // Generate g_tbls from computed decode matrix (k x k) matrix + g_tbls = malloc(sizeof(unsigned char) * (k * m * 32)); + if (NULL == g_tbls) { + goto out; + } + + inverse_rows = get_lrc_inverse_rows(k, m, 0, k, 0, decode_inverse, isa_l_desc->matrix, missing_bm, isa_l_desc->gf_mul); + + decoded_elements = (unsigned char**)malloc(sizeof(unsigned char*)*num_missing_elements); + if (NULL == decoded_elements) { + goto out; + } + + available_fragments = (unsigned char**)malloc(sizeof(unsigned char*)*k); + if (NULL == available_fragments) { + goto out; + } + + uint64_t missing_local_parities = 0; + for (j = n - local_parity; j < n; j++) { + missing_local_parities |= (missing_bm & (1 << j)); + } + + for (i = 0, j = 0; i < n - local_parity && j < k; i++) { + if (missing_bm & (1 << i)) { + continue; + } + if (i < k) { + available_fragments[j] = (unsigned char*)data[i]; + j++; + } else { + if (used_idxs[i]) { + available_fragments[j] = (unsigned char*)parity[i - k]; + j++; + } + } + } + if (j < k && !missing_local_parities && use_combined_parity) { + combined_local_parities = calloc(blocksize, sizeof(unsigned char)); + if (NULL == combined_local_parities) { + goto out; + } + for (i = n - local_parity; i < n; i++) { + for (int x = 0; x < blocksize; x++) { + combined_local_parities[x] ^= parity[i - k][x]; + } + } + available_fragments[j] = combined_local_parities; + j++; + } + for (i = n - local_parity; i < n && j < k; i++) { + if (used_idxs[i]) { + available_fragments[j] = (unsigned char*)parity[i-k]; + j++; + } + } + // Grab pointers to memory needed for missing data fragments + j = 0; + for (i = 0; i < k; i++) { + if (missing_bm & (1 << i)) { + decoded_elements[j] = (unsigned char*)data[i]; + j++; + } + } + for (i = k; i < n; i++) { + if (missing_bm & (1 << i)) { + decoded_elements[j] = (unsigned char*)parity[i - k]; + j++; + } + } + + isa_l_desc->ec_init_tables(k, num_missing_elements, inverse_rows, g_tbls); + + isa_l_desc->ec_encode_data(blocksize, k, num_missing_elements, g_tbls, available_fragments, + decoded_elements); + + ret = 0; + +out: + free(g_tbls); + free(combined_local_parities); + free(decode_matrix); + free(decode_inverse); + free(inverse_rows); + free(decoded_elements); + free(available_fragments); + free(used_idxs); + + return ret; +} + +static unsigned char* isa_l_lrc_get_reconstruct_matrix( + int k, int m, unsigned local_parity, int destination_idx, + unsigned char *encode_matrix, uint64_t *missing_bm, int *used_idxs, + int *min_col, int *max_col, int *mx_size, int *missing_local_parity, int *use_combined_parity) +{ + unsigned char *decode_matrix = NULL; + uint64_t useful_mask = 0; + + int min_range=0, max_range=0; + if (destination_idx < k) { + // reconstructing a data frag; see if we can stay local + int local_group = local_group_for_data(k, local_parity, destination_idx); + min_range = local_group_data_lower(k, local_parity, local_group); + max_range = local_group_data_upper(k, local_parity, local_group); + int local_parity_idx = k + m - local_parity + local_group; + int missing_local = (1 << local_parity_idx) & *missing_bm; + for (int i = min_range; !missing_local && i < max_range; i++) { + useful_mask |= 1 << i; + if (i == destination_idx) { + // We already knew we were missing *that* one... + continue; + } + missing_local |= (1 << i) & *missing_bm; + } + if (!missing_local) { + // We have everything we need! + useful_mask |= 1 << local_parity_idx; + *missing_bm &= useful_mask; + *mx_size = max_range - min_range; + decode_matrix = malloc(sizeof(unsigned char) * (*mx_size) * (*mx_size)); + if (NULL == decode_matrix) { + return NULL; + } + int col = 0; + for (int enc_idx = min_range; enc_idx < max_range; enc_idx++) { + if (enc_idx == destination_idx) + continue; + for (int j = min_range; j < max_range; j++) { + decode_matrix[(*mx_size * col) + j - min_range] = encode_matrix[(k * enc_idx) + j]; + } + used_idxs[enc_idx] = 1; + col++; + } + // add local parity + for (int j = min_range; j < max_range; j++) { + decode_matrix[*mx_size * col + j - min_range] = encode_matrix[(k * local_parity_idx) + j]; + } + used_idxs[local_parity_idx] = 1; + } + } + if (destination_idx >= k + m - local_parity) { + // reconstructing a local parity frag; see if we can use local data + int local_group = destination_idx - k - m + local_parity; + min_range = local_group_data_lower(k, local_parity, local_group); + max_range = local_group_data_upper(k, local_parity, local_group); + int missing_local = 0; + for (int i = min_range; !missing_local && i < max_range; i++) { + useful_mask |= 1 << i; + missing_local |= (1 << i) & *missing_bm; + } + if (!missing_local) { + // We have everything we need! + useful_mask |= 1 << destination_idx; + *missing_bm &= useful_mask; + *missing_local_parity = 1; + *mx_size = max_range - min_range; + decode_matrix = malloc(sizeof(unsigned char) * (*mx_size) * (*mx_size)); + if (NULL == decode_matrix) { + return NULL; + } + for (int i = min_range; i < max_range; i++) { + for (int j = min_range; j < max_range; j++) { + decode_matrix[*mx_size * (i - min_range) + j - min_range] = encode_matrix[(k * i) + j]; + } + used_idxs[i] = 1; + } + } + } + + if (decode_matrix == NULL) { + decode_matrix = isa_l_lrc_get_decode_matrix(k, m, local_parity, + encode_matrix, *missing_bm, used_idxs, use_combined_parity); + if (decode_matrix) { + *mx_size = k; + } + min_range = 0; + max_range = k; + } + + *min_col = min_range; + *max_col = max_range; + return decode_matrix; +} + +static int isa_l_lrc_reconstruct(void *desc, char **data, char **parity, + int *missing_idxs, int destination_idx, int blocksize) +{ + isa_l_descriptor *isa_l_desc = (isa_l_descriptor*) desc; + unsigned char *g_tbls = NULL; + unsigned char *decode_matrix = NULL; + unsigned char *decode_inverse = NULL; + unsigned char *inverse_rows = NULL; + unsigned char *reconstruct_buf = NULL; + unsigned char **available_fragments = NULL; + int k = isa_l_desc->k; + int m = isa_l_desc->m; + int local_parity = isa_l_desc->l; + int n = k + m; + int ret = -1; + int i, j; + uint64_t missing_bm = convert_list_to_bitmap(missing_idxs); + int inverse_row = -1; + int min_range = 0; + int max_range = 0; + int matrix_size = k; + int *used_idxs = calloc(n, sizeof(int)); + int missing_local_parity = 0; + unsigned char * combined_local_parities = NULL; + int use_combined_parity = 0; + + if( NULL == used_idxs) { + goto out; + } + /** + * Get available elements and compute the inverse of their + * corresponding rows. + */ + decode_matrix = isa_l_lrc_get_reconstruct_matrix(k, m, local_parity, destination_idx, isa_l_desc->matrix, &missing_bm, used_idxs, &min_range, &max_range, &matrix_size, &missing_local_parity, &use_combined_parity); + if (NULL == decode_matrix) { + goto out; + } + + decode_inverse = (unsigned char*)malloc(sizeof(unsigned char) * matrix_size * matrix_size); + + if (NULL == decode_inverse) { + goto out; + } + + int im_ret = isa_l_desc->gf_invert_matrix(decode_matrix, decode_inverse, matrix_size); + if (im_ret < 0) { + goto out; + } + + int nb_parity = (matrix_size == k)? m: 1; + unsigned char * encode = (matrix_size == k || missing_local_parity)? isa_l_desc->matrix:decode_matrix; + + /** + * Get the row needed to reconstruct + */ + inverse_rows = get_lrc_inverse_rows(k, m, min_range, max_range, missing_local_parity, decode_inverse, encode, missing_bm, isa_l_desc->gf_mul); + + // Generate g_tbls from computed decode matrix (k x k) matrix + g_tbls = malloc(sizeof(unsigned char) * (matrix_size * nb_parity * 32)); + if (NULL == g_tbls) { + goto out; + } + + /** + * Fill in the available elements + */ + available_fragments = (unsigned char**)malloc(sizeof(unsigned char*)*matrix_size); + if (NULL == available_fragments) { + goto out; + } + + j = 0; + if (matrix_size == k) { + for (i = 0; i < n - local_parity && j < k; i++) { + if (missing_bm & (1 << i)) { + continue; + } + if (i < k) { + available_fragments[j] = (unsigned char*)data[i]; + j++; + } else { + if (used_idxs[i]) { + available_fragments[j] = (unsigned char*)parity[i - k]; + j++; + } + } + } + if (j < k && !missing_local_parity && use_combined_parity) { + combined_local_parities = calloc(blocksize, sizeof(unsigned char)); + if (NULL == combined_local_parities) { + goto out; + } + for (i = n - local_parity; i < n; i++) { + for (int x = 0; x < blocksize; x++) { + combined_local_parities[x] ^= parity[i - k][x]; + } + } + available_fragments[j] = combined_local_parities; + j++; + } + for (i = n - local_parity; i < n && j < k; i++) { + if (missing_bm & (1 << i)) { + continue; + } + if (used_idxs[i]) { + available_fragments[j] = (unsigned char*)parity[i - k]; + j++; + } + } + } else { + for (i = 0; i < n; i++) { + if (used_idxs[i]) { + if (i ec_init_tables(matrix_size, 1, &inverse_rows[inverse_row * matrix_size], g_tbls); + isa_l_desc->ec_encode_data(blocksize, matrix_size, 1, g_tbls, available_fragments, + &reconstruct_buf); + + ret = 0; +out: + free(g_tbls); + free(combined_local_parities); + free(decode_matrix); + free(decode_inverse); + free(inverse_rows); + free(available_fragments); + free(used_idxs); + + return ret; +} + +static struct ec_backend_op_stubs isa_l_rs_lrc_op_stubs = { + .INIT = isa_l_rs_lrc_init, + .EXIT = isa_l_exit, + .ISSYSTEMATIC = 1, + .ENCODE = isa_l_encode, + .DECODE = isa_l_lrc_decode, + .FRAGSNEEDED = isa_l_min_fragments, + .RECONSTRUCT = isa_l_lrc_reconstruct, + .ELEMENTSIZE = isa_l_element_size, + .ISCOMPATIBLEWITH = isa_l_rs_lrc_is_compatible_with, + .GETMETADATASIZE = get_backend_metadata_size_zero, + .GETENCODEOFFSET = get_encode_offset_zero, + .CHECKRECONSTRUCTFRAGMENTS = isa_l_rs_lrc_check_reconstruct_fragments, +}; + +__attribute__ ((visibility ("internal"))) +struct ec_backend_common backend_isa_l_rs_lrc = { + .id = EC_BACKEND_ISA_L_RS_LRC, + .name = ISA_L_RS_LRC_LIB_NAME, + .soname = ISA_L_RS_LRC_SO_NAME, + .soversion = ISA_L_RS_LRC_LIB_VER_STR, + .ops = &isa_l_rs_lrc_op_stubs, + .ec_backend_version = _VERSION(ISA_L_RS_LRC_LIB_MAJOR, + ISA_L_RS_LRC_LIB_MINOR, + ISA_L_RS_LRC_LIB_REV), +}; diff --git a/src/erasurecode.c b/src/erasurecode.c index 4312cb3..e0b76dd 100644 --- a/src/erasurecode.c +++ b/src/erasurecode.c @@ -53,6 +53,7 @@ extern struct ec_backend_common backend_liberasurecode_rs_vand; extern struct ec_backend_common backend_isa_l_rs_cauchy; extern struct ec_backend_common backend_libphazr; extern struct ec_backend_common backend_isa_l_rs_vand_inv; +extern struct ec_backend_common backend_isa_l_rs_lrc; static ec_backend_t ec_backends_supported[] = { (ec_backend_t) &backend_null, @@ -65,6 +66,7 @@ static ec_backend_t ec_backends_supported[] = { (ec_backend_t) &backend_isa_l_rs_cauchy, (ec_backend_t) &backend_libphazr, (ec_backend_t) &backend_isa_l_rs_vand_inv, + (ec_backend_t) &backend_isa_l_rs_lrc, NULL, }; diff --git a/test/liberasure_rs_isal_stress_test.c b/test/liberasure_rs_isal_stress_test.c index e6e96ba..7f3189f 100644 --- a/test/liberasure_rs_isal_stress_test.c +++ b/test/liberasure_rs_isal_stress_test.c @@ -35,8 +35,10 @@ #include "erasurecode_helpers_ext.h" #include "erasurecode_preprocessing.h" +static int total_decoding_errors = 0; +static int total_reconstrcut_errors = 0; -char *create_buffer(int size, int fill) +char *create_buffer(int size) { char *buf = malloc(size); if (buf == NULL) { @@ -62,9 +64,7 @@ int *create_skips_array(struct ec_args *args, int skip) return NULL; } memset(buf, 0, array_size); - if (skip >= 0 && skip < num) { - buf[skip] = 1; - } + return buf; } @@ -108,7 +108,7 @@ out: return num_frags; } -static void encode_decode_test_impl(const ec_backend_id_t be_id, +int encode_decode_test_impl(const ec_backend_id_t be_id, struct ec_args *args, int *skip) { @@ -131,15 +131,15 @@ static void encode_decode_test_impl(const ec_backend_id_t be_id, if (-EBACKENDNOTAVAIL == desc) { fprintf(stderr, "Backend library not available!\n"); - return; + return desc; } else if ((args->k + args->m) > EC_MAX_FRAGMENTS) { fprintf(stderr, "data + parity is greater than %d!\n", EC_MAX_FRAGMENTS); assert(-EINVALIDPARAMS == desc); - return; + return desc; } else assert(desc > 0); - orig_data = create_buffer(orig_data_size, 'x'); + orig_data = create_buffer(orig_data_size); assert(orig_data != NULL); rc = liberasurecode_encode(desc, orig_data, orig_data_size, &encoded_data, &encoded_parity, &encoded_fragment_len); @@ -179,6 +179,11 @@ static void encode_decode_test_impl(const ec_backend_id_t be_id, rc = liberasurecode_decode(desc, avail_frags, num_avail_frags, encoded_fragment_len, 1, &decoded_data, &decoded_data_len); + if (rc == -1 ) { + total_decoding_errors++; + return rc; + } + assert(0 == rc); assert(decoded_data_len == orig_data_size); assert(memcmp(decoded_data, orig_data, orig_data_size) == 0); @@ -192,6 +197,7 @@ static void encode_decode_test_impl(const ec_backend_id_t be_id, assert(0 == liberasurecode_instance_destroy(desc)); free(orig_data); free(avail_frags); + return rc; } @@ -199,6 +205,11 @@ static void test_decode_with_missing_multi_data_parity( const ec_backend_id_t be_id, struct ec_args *args, uint64_t nb_iter) { uint64_t i; + int max_num_missing = args->m; + if (be_id == EC_BACKEND_ISA_L_RS_LRC) { + max_num_missing = (args->m - args->priv_args1.lrc_args.l + 1); + } + for (i = 0; i < nb_iter; i++) { int *skip = create_skips_array(args,-1); assert(skip != NULL); @@ -210,38 +221,153 @@ static void test_decode_with_missing_multi_data_parity( skip[value] = 1; count++; } - - if(count == args->m) { + if(count == max_num_missing) { break; } - } - encode_decode_test_impl(be_id, args, skip); + } + + int err = encode_decode_test_impl(be_id, args, skip); + if (err) { + printf("skip\n"); + for (int i = 0; i < args->k + args->m; i++) + if (skip[i]) + printf("i= %d val=%d ",i , skip[i]); + //break; + } free(skip); } + printf("total decoding errors %d\n", total_decoding_errors); } +static int reconstruct_test_impl(const ec_backend_id_t be_id, + struct ec_args *args, + int *skip) +{ + int rc = 0; + int desc = -1; + int orig_data_size = 1024 * 1024; + char *orig_data = NULL; + char **encoded_data = NULL, **encoded_parity = NULL; + uint64_t encoded_fragment_len = 0; + char **avail_frags = NULL; + int num_avail_frags = 0; + int i = 0; + char *out = NULL; + + desc = liberasurecode_instance_create(be_id, args); + if (-EBACKENDNOTAVAIL == desc) { + fprintf(stderr, "Backend library not available!\n"); + return -1; + } + assert(desc > 0); + + orig_data = create_buffer(orig_data_size); + assert(orig_data != NULL); + rc = liberasurecode_encode(desc, orig_data, orig_data_size, + &encoded_data, &encoded_parity, &encoded_fragment_len); + assert(rc == 0); + out = malloc(encoded_fragment_len); + assert(out != NULL); + char *cmp = NULL; + + num_avail_frags = create_frags_array(&avail_frags, encoded_data, encoded_parity, args, skip); + + if (i < args->k) { + cmp = encoded_data[i]; + } + else { + cmp = encoded_parity[i - args->k]; + } + memset(out, 0, encoded_fragment_len); + rc = liberasurecode_reconstruct_fragment(desc, avail_frags, num_avail_frags, encoded_fragment_len, i, out); + if (rc == -1 ) { + total_reconstrcut_errors++; + return rc; + } + assert(rc == 0); + for (int x= 0; x < encoded_fragment_len; x++){ + if (out[x] != cmp[x]){ + printf(" out(%c) x(%d) \n", out[x], x); + total_reconstrcut_errors++; + break; + } + } + assert(memcmp(out, cmp, encoded_fragment_len) == 0); + free(avail_frags); + + free(orig_data); + free(out); + liberasurecode_encode_cleanup(desc, encoded_data, encoded_parity); + liberasurecode_instance_destroy(desc); + return rc; +} + +static void test_multi_reconstruct(const ec_backend_id_t be_id, + struct ec_args *args, int nb_iter) +{ + int i = 0; + + int max_num_missing = args->m; + if (be_id == EC_BACKEND_ISA_L_RS_LRC) { + max_num_missing = (args->m - args->priv_args1.lrc_args.l + 1); + } + + for (i = 0; i < nb_iter; i++) { + int *skip = create_skips_array(args,-1); + assert(skip != NULL); + int count = 0; + while(true) { + int value = rand() % (args->k + args->m) ; + + if (skip[value] != 1){ + skip[value] = 1; + count++; + } + if(count == max_num_missing) { + break; + } + } + + int err = reconstruct_test_impl(be_id, args, skip); + if (err) { + printf("skip\n"); + for (int i = 0; i < args->k + args->m; i++) + if (skip[i]) + printf("i= %d val=%d ",i , skip[i]); + //break; + } + free(skip); + } + printf("total reconstruct errors %d\n", total_decoding_errors); + + +} int main(int argc, char **argv) { - if (argc != 4) { + if (argc != 6) { printf("Stress Test with m missing positions for RS(k,m).\n"); printf("Number of iterations {nb_iter} should be at least ~ (k+m)!/(k!)(m!).\n"); - printf("Usage: %s \n", argv[0]); + printf("Usage: %s \n", argv[0]); return 0; } - int k = atoi(argv[1]); - int m = atoi(argv[2]); - u_int64_t nb_iter = atoll(argv[3]); + int backend = atoi(argv[1]); + int k = atoi(argv[2]); + int m = atoi(argv[3]); + int l = atoi(argv[4]); + + u_int64_t nb_iter = atoll(argv[5]); struct ec_args isa_l_km_args = { .k = k, .m = m, .w = 8, .hd = m+1, + .priv_args1.lrc_args.l = l, }; // Test is ok if no Assertion `0 == rc' occurs // We can make backend configurable and test EC_BACKEND_ISA_L_RS_VAND - test_decode_with_missing_multi_data_parity(EC_BACKEND_ISA_L_RS_VAND_INV, &isa_l_km_args, nb_iter); - + test_decode_with_missing_multi_data_parity(backend, &isa_l_km_args, nb_iter); + test_multi_reconstruct(backend, &isa_l_km_args, nb_iter); return 0; } diff --git a/test/liberasurecode_test.c b/test/liberasurecode_test.c index d46c6e1..3908b0c 100644 --- a/test/liberasurecode_test.c +++ b/test/liberasurecode_test.c @@ -43,6 +43,7 @@ #define JERASURE_RS_CAUCHY_BACKEND "jerasure_rs_cauchy" #define ISA_L_RS_VAND_BACKEND "isa_l_rs_vand" #define ISA_L_RS_VAND_INV_BACKEND "isa_l_rs_vand_inv" +#define ISA_L_RS_LRC_BACKEND "isa_l_rs_lrc" #define ISA_L_RS_CAUCHY_BACKEND "isa_l_rs_cauchy" #define SHSS_BACKEND "shss" #define RS_VAND_BACKEND "liberasurecode_rs_vand" @@ -197,6 +198,37 @@ struct ec_args *isa_l_test_args[] = { &isa_l_args, &isa_l_85_args, NULL }; +struct ec_args isa_l_lrc_75_args = { + .k = 7, + .m = 5, + .w = 8, + .hd =6, + .priv_args1.lrc_args.l = 2, +}; +struct ec_args isa_l_lrc_84_args = { + .k = 8, + .m = 4, + .w = 8, + .hd = 5, + .priv_args1.lrc_args.l = 2, +}; + +struct ec_args isa_l_lrc_123_args = { + .k = 12, + .m = 12, + .w = 8, + .hd =13, + .priv_args1.lrc_args.l = 3, +}; +struct ec_args isa_l_lrc_155_args = { + .k = 15, + .m = 5, + .w = 8, + .hd =5, + .priv_args1.lrc_args.l = 2, +}; +struct ec_args *isa_l_lrc_test_args[] = { &isa_l_lrc_75_args, &isa_l_lrc_84_args, &isa_l_lrc_123_args, + &isa_l_lrc_155_args, NULL }; int priv = 128; struct ec_args shss_args = { .k = 6, @@ -316,6 +348,8 @@ char * get_name_from_backend_id(ec_backend_id_t be) { return ISA_L_RS_VAND_BACKEND; case EC_BACKEND_ISA_L_RS_VAND_INV: return ISA_L_RS_VAND_INV_BACKEND; + case EC_BACKEND_ISA_L_RS_LRC: + return ISA_L_RS_LRC_BACKEND; case EC_BACKEND_ISA_L_RS_CAUCHY: return ISA_L_RS_CAUCHY_BACKEND; case EC_BACKEND_SHSS: @@ -358,6 +392,9 @@ struct ec_args *create_ec_args(ec_backend_id_t be, ec_checksum_type_t ct, int ba case EC_BACKEND_ISA_L_RS_VAND_INV: backend_args_array = isa_l_test_args; break; + case EC_BACKEND_ISA_L_RS_LRC: + backend_args_array = isa_l_lrc_test_args; + break; case EC_BACKEND_ISA_L_RS_CAUCHY: backend_args_array = isa_l_test_args; break; @@ -1292,7 +1329,7 @@ static void encode_decode_test_impl(const ec_backend_id_t be_id, * and 5 are assumed unavailable. * * We only mark at most 2 as unavailable, as we cannot guarantee every situation - * will be able to habndle 3 failures. + * will be able to handle 3 failures. */ static void reconstruct_test_impl(const ec_backend_id_t be_id, struct ec_args *args, @@ -1607,8 +1644,13 @@ static void test_decode_with_missing_parity(const ec_backend_id_t be_id, static void test_decode_with_missing_multi_data(const ec_backend_id_t be_id, struct ec_args *args) { - int max_num_missing = args->k <= (args->hd - 1) ? args->k : args->hd - 1; + int i,j; + int max_num_missing = args->k <= (args->hd - 1) ? args->k : args->hd - 1; + // for LRC backend, we test up to g+1 missing data, g is the nubmer of global parities. + if (be_id == EC_BACKEND_ISA_L_RS_LRC) { + max_num_missing = (args->m - args->priv_args1.lrc_args.l + 1); + } for (i = 0; i < args->k - max_num_missing + 1; i++) { int *skip = create_skips_array(args,-1); assert(skip != NULL); @@ -1641,6 +1683,9 @@ static void test_decode_with_missing_multi_data_parity( { int i,j; int max_num_missing = args->hd - 1; + if (be_id == EC_BACKEND_ISA_L_RS_LRC) { + max_num_missing = (args->m - args->priv_args1.lrc_args.l + 1); + } int end = (args->k + args->m) - max_num_missing + 1; for (i = 0; i < end; i++) { int *skip = create_skips_array(args,-1); @@ -2272,6 +2317,112 @@ static void test_metadata_crcs_be(void) assert(is_invalid_fragment_header((fragment_header_t *) header) == 1); } +static void test_isa_l_rs_lrc_local_only_reconstruct(void) +{ + int rc = 0; + int desc = -1; + int orig_data_size = 1024 * 1024; + char *orig_data = NULL; + char **encoded_data = NULL, **encoded_parity = NULL; + uint64_t encoded_fragment_len = 0; + char **avail_frags = NULL; + int num_avail_frags = 0; + char *out = NULL; + int skip[] = { + 0, 0, 0, 0, 0, 0, 0, 1, // local data group 1 + 1, 1, 1, 1, 1, 1, 1, // local data group 2 + 1, 1, 1, // global parities + 0, 1, // local parities + }; + + desc = liberasurecode_instance_create(EC_BACKEND_ISA_L_RS_LRC, &isa_l_lrc_155_args); + if (-EBACKENDNOTAVAIL == desc) { + fprintf(stderr, "Backend library not available!\n"); + return; + } + assert(desc > 0); + + orig_data = create_buffer(orig_data_size, 'x'); + assert(orig_data != NULL); + rc = liberasurecode_encode(desc, orig_data, orig_data_size, + &encoded_data, &encoded_parity, &encoded_fragment_len); + assert(rc == 0); + out = malloc(encoded_fragment_len); + assert(out != NULL); + char *cmp = encoded_data[7]; + num_avail_frags = create_frags_array(&avail_frags, encoded_data, + encoded_parity, &isa_l_lrc_155_args, skip); + memset(out, 0, encoded_fragment_len); + rc = liberasurecode_reconstruct_fragment( + desc, avail_frags, num_avail_frags, encoded_fragment_len, 7, out); + assert(rc == 0); + assert(memcmp(out, cmp, encoded_fragment_len) == 0); + free(avail_frags); + + free(orig_data); + free(out); + liberasurecode_encode_cleanup(desc, encoded_data, encoded_parity); + liberasurecode_instance_destroy(desc); +} + +static void test_isa_l_rs_lrc_combine_local_frags(void) +{ + int rc = 0; + int desc = -1; + int orig_data_size = 1024 * 1024; + char *orig_data = NULL; + char **encoded_data = NULL, **encoded_parity = NULL; + uint64_t encoded_fragment_len = 0; + char *decoded_data = NULL; + uint64_t decoded_data_len = 0; + char **avail_frags = NULL; + int num_avail_frags = 0; + char *out = NULL; + int skip[] = { + 0, 0, 1, 0, 0, 0, 0, 1, // local data group 1 + 0, 0, 1, 0, 1, 0, 0, // local data group 2 + 0, 0, 0, // global parities + 0, 0, // local parities + }; + + desc = liberasurecode_instance_create(EC_BACKEND_ISA_L_RS_LRC, &isa_l_lrc_155_args); + if (-EBACKENDNOTAVAIL == desc) { + fprintf(stderr, "Backend library not available!\n"); + return; + } + assert(desc > 0); + + orig_data = create_buffer(orig_data_size, 'x'); + assert(orig_data != NULL); + rc = liberasurecode_encode(desc, orig_data, orig_data_size, + &encoded_data, &encoded_parity, &encoded_fragment_len); + assert(rc == 0); + out = malloc(encoded_fragment_len); + assert(out != NULL); + num_avail_frags = create_frags_array(&avail_frags, encoded_data, + encoded_parity, &isa_l_lrc_155_args, skip); + memset(out, 0, encoded_fragment_len); + + rc = liberasurecode_decode(desc, avail_frags, num_avail_frags, + encoded_fragment_len, 1, + &decoded_data, &decoded_data_len); + assert(0 == rc); + assert(decoded_data_len == orig_data_size); + assert(memcmp(decoded_data, orig_data, orig_data_size) == 0); + + rc = liberasurecode_reconstruct_fragment( + desc, avail_frags, num_avail_frags, encoded_fragment_len, 2, out); + assert(rc == 0); + assert(memcmp(out, encoded_data[2], encoded_fragment_len) == 0); + free(avail_frags); + + free(orig_data); + free(out); + liberasurecode_encode_cleanup(desc, encoded_data, encoded_parity); + liberasurecode_decode_cleanup(desc, decoded_data); + liberasurecode_instance_destroy(desc); +} + //static void test_verify_str /* An individual test, useful to ensure the reported name @@ -2346,6 +2497,10 @@ struct testcase testcases[] = { // ISA-L rs vand inv tests TEST_SUITE(EC_BACKEND_ISA_L_RS_VAND_INV), TEST({.with_args = test_decode_with_missing_multi_data_parity_fail_with_isal}, EC_BACKEND_ISA_L_RS_VAND_INV, 0), + // ISA-L rs lrc tests + TEST_SUITE(EC_BACKEND_ISA_L_RS_LRC), + TEST({.no_args = test_isa_l_rs_lrc_local_only_reconstruct}, EC_BACKENDS_MAX, 0), + TEST({.no_args = test_isa_l_rs_lrc_combine_local_frags}, EC_BACKENDS_MAX, 0), // shss tests TEST_SUITE(EC_BACKEND_SHSS), // Internal RS Vand backend tests