]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
erasure-code: Add optimization flags for plugins
authorJamie Pryde <jamiepry@uk.ibm.com>
Tue, 15 Oct 2024 14:30:13 +0000 (14:30 +0000)
committerAlex Ainscow <aainscow@uk.ibm.com>
Wed, 19 Mar 2025 11:03:55 +0000 (11:03 +0000)
Add flags to erasure code plugins to report supported optimizations
and new unittest_erasure_code_plugins to test these flags for many
permutations of plugins and profiles.

Signed-off-by: Bill Scales <bill_scales@uk.ibm.com>
[Taken from Bill's fork with permission]
Signed-off-by: Jamie Pryde <jamiepry@uk.ibm.com>
src/erasure-code/ErasureCodeInterface.h
src/erasure-code/clay/ErasureCodeClay.h
src/erasure-code/isa/ErasureCodeIsa.h
src/erasure-code/jerasure/ErasureCodeJerasure.h
src/erasure-code/lrc/ErasureCodeLrc.h
src/erasure-code/shec/ErasureCodeShec.h
src/test/erasure-code/CMakeLists.txt
src/test/erasure-code/ErasureCodeExample.h
src/test/erasure-code/TestErasureCode.cc
src/test/erasure-code/TestErasureCodePlugins.cc [new file with mode: 0644]

index bd7c2c44302f5c1d0cca375da2de5c130405817f..8420df40f3e22ff62d4647b7d56aa9a4d0366a11 100644 (file)
@@ -476,6 +476,78 @@ namespace ceph {
     virtual int decode_concat(const std::map<int, bufferlist> &chunks,
                              bufferlist *decoded) = 0;
 
+       using plugin_flags = uint64_t;
+
+    /**
+     * Return a set of flags indicating which EC optimizations are supported
+     * by the plugin.
+     *
+     * @return logical OR of the supported performance optimizations
+     */
+    virtual plugin_flags get_supported_optimizations() const = 0;
+    enum {
+      /* Partial read optimization assumes that the erasure code is systematic
+       * and that concatenating the data chunks in the order returned by
+       * get_chunk_mapping will create the data encoded for a stripe. The
+       * optimization permits small reads to read data directly from the data
+       * chunks without calling decode.
+       */
+      FLAG_EC_PLUGIN_PARTIAL_READ_OPTIMIZATION = 1<<0,
+      /* Partial write optimization assumes that a write to less than one
+       * chunk only needs to read this fragment from each data chunk in the
+       * stripe and can then use encode to create the corresponding coding
+       * fragments.
+       */
+      FLAG_EC_PLUGIN_PARTIAL_WRITE_OPTIMIZATION = 1<<1,
+      /* Zero input zero output optimization means the erasure code has the
+       * property that if all the data chunks are zero then the coding parity
+       * chunks will also be zero.
+       */
+      FLAG_EC_PLUGIN_ZERO_INPUT_ZERO_OUTPUT_OPTIMIZATION = 1<<2,
+      /* Zero padding optimization permits the encode and decode methods to
+       * be called with buffers that are zero length. The plugin treats
+       * this as a chunk of all zeros.
+       */
+      FLAG_EC_PLUGIN_ZERO_PADDING_OPTIMIZATION = 1<<3,
+      /* Parity delta write optimization means the encode_delta and
+       * apply_delta methods are supported which allows small updates
+       * to a stripe to be applied using a read-modify-write of a
+       * data chunk and the coding parity chunks.
+       */
+      FLAG_EC_PLUGIN_PARITY_DELTA_OPTIMIZATION = 1<<4,
+    };
+    static const char *get_optimization_flag_name(const plugin_flags flag) {
+      switch (flag) {
+      case FLAG_EC_PLUGIN_PARTIAL_READ_OPTIMIZATION: return "partialread";
+      case FLAG_EC_PLUGIN_PARTIAL_WRITE_OPTIMIZATION: return "partialwrite";
+      case FLAG_EC_PLUGIN_ZERO_INPUT_ZERO_OUTPUT_OPTIMIZATION: return "zeroinout";
+      case FLAG_EC_PLUGIN_ZERO_PADDING_OPTIMIZATION: return "zeropadding";
+      case FLAG_EC_PLUGIN_PARITY_DELTA_OPTIMIZATION: return "paritydelta";
+      default: return "???";
+      }
+    }
+    static std::string get_optimization_flags_string(plugin_flags flags) {
+      std::string s;
+      for (unsigned n=0; flags && n<64; ++n) {
+       if (flags & (1ull << n)) {
+                       if (s.length())
+                               s += ",";
+                       s += get_optimization_flag_name(1ull << n);
+                       flags -= flags & (1ull << n);
+               }
+      }
+      return s;
+    }
+
+    /**
+     * Return a string describing which EC optimizations are supported
+     * by the plugin.
+     *
+     * @return string of optimizations supported by the plugin
+     */
+    virtual std::string get_optimizations_flags_string() const {
+      return get_optimization_flags_string(get_supported_optimizations());
+    }
   };
 
   typedef std::shared_ptr<ErasureCodeInterface> ErasureCodeInterfaceRef;
index f847d090e4f0da3ad425023468ee26acbc51c2a9..77216cd922e707d3a286562e48a6b9bc9f298273 100644 (file)
@@ -46,6 +46,18 @@ public:
 
   ~ErasureCodeClay() override;
 
+  uint64_t get_supported_optimizations() const override {
+    if (m == 1) {
+      // PARTIAL_WRITE optimization can be supported in
+      // the corner case of m = 1
+      return FLAG_EC_PLUGIN_PARTIAL_READ_OPTIMIZATION |
+       FLAG_EC_PLUGIN_PARTIAL_WRITE_OPTIMIZATION |
+       FLAG_EC_PLUGIN_ZERO_INPUT_ZERO_OUTPUT_OPTIMIZATION;
+    }
+    return FLAG_EC_PLUGIN_PARTIAL_READ_OPTIMIZATION |
+      FLAG_EC_PLUGIN_ZERO_INPUT_ZERO_OUTPUT_OPTIMIZATION;
+  }
+
   unsigned int get_chunk_count() const override {
     return k+m;
   }
index d38b996a84a5c716e53f21fc93041bf844f4118e..21ab3c645f553bc01dcd0493f3917c0eade93988 100644 (file)
@@ -64,6 +64,12 @@ public:
   {
   }
 
+  uint64_t get_supported_optimizations() const override {
+    return FLAG_EC_PLUGIN_PARTIAL_READ_OPTIMIZATION |
+      FLAG_EC_PLUGIN_PARTIAL_WRITE_OPTIMIZATION |
+      FLAG_EC_PLUGIN_ZERO_INPUT_ZERO_OUTPUT_OPTIMIZATION;
+  }
+
   unsigned int
   get_chunk_count() const override
   {
index 48e98747ad65ff03638550440e8b3bc1bc81de08..f0c8ffb6c4dd09153b2b6d698febcf2d6c91da9c 100644 (file)
@@ -45,6 +45,13 @@ public:
   {}
 
   ~ErasureCodeJerasure() override {}
+
+  uint64_t get_supported_optimizations() const override {
+    return FLAG_EC_PLUGIN_PARTIAL_READ_OPTIMIZATION |
+      FLAG_EC_PLUGIN_PARTIAL_WRITE_OPTIMIZATION |
+      FLAG_EC_PLUGIN_ZERO_INPUT_ZERO_OUTPUT_OPTIMIZATION;
+  }
+
   
   unsigned int get_chunk_count() const override {
     return k + m;
index 7a41014a27ccd18e8b80aa095619116dcfe9a355..4bc44c32f6eb698e50d41cbe22e17d7a526e9405 100644 (file)
@@ -95,6 +95,12 @@ public:
                             CrushWrapper &crush,
                             std::ostream *ss) const override;
 
+  uint64_t get_supported_optimizations() const override {
+    return FLAG_EC_PLUGIN_PARTIAL_READ_OPTIMIZATION |
+      FLAG_EC_PLUGIN_PARTIAL_WRITE_OPTIMIZATION |
+      FLAG_EC_PLUGIN_ZERO_INPUT_ZERO_OUTPUT_OPTIMIZATION;
+  }
+
   unsigned int get_chunk_count() const override {
     return chunk_count;
   }
index 1440b4d29377181d0bd4760fca09bfc1ea394752..2ce7020cbcfecc7dcf2d5c2d3153dab5a0d66f0f 100644 (file)
@@ -61,6 +61,12 @@ public:
 
   ~ErasureCodeShec() override {}
 
+  uint64_t get_supported_optimizations() const override {
+    return FLAG_EC_PLUGIN_PARTIAL_READ_OPTIMIZATION |
+      FLAG_EC_PLUGIN_PARTIAL_WRITE_OPTIMIZATION |
+      FLAG_EC_PLUGIN_ZERO_INPUT_ZERO_OUTPUT_OPTIMIZATION;
+  }
+
   unsigned int get_chunk_count() const override {
     return k + m;
   }
index ab7328eae0ebb2c4a7a0c50f022eae1ca69ba6a9..429d29d536adf6b50133c485de9a508be6fe3a9c 100644 (file)
@@ -62,6 +62,25 @@ target_link_libraries(unittest_erasure_code
   ceph-common
   )
 
+if(WITH_EC_ISA_PLUGIN)
+# unittest_erasure_code_plugins
+add_executable(unittest_erasure_code_plugins
+  TestErasureCodePlugins.cc
+  $<TARGET_OBJECTS:unit-main>
+  )
+add_ceph_unittest(unittest_erasure_code_plugins)
+add_dependencies(unittest_erasure_code_plugins
+  ec_jerasure
+  ec_isa
+  ec_lrc
+  ec_shec
+  ec_clay)
+target_link_libraries(unittest_erasure_code_plugins
+  global
+  ${CMAKE_DL_LIBS}
+  ceph-common)
+endif(WITH_EC_ISA_PLUGIN)
+  
 # unittest_erasure_code_plugin_jerasure
 add_executable(unittest_erasure_code_plugin_jerasure
   TestErasureCodePluginJerasure.cc
index c036199c5d9b0883952609dc98e2d1db68460a46..18247105d7cf0be1e52351abcbaab883d3c947d7 100644 (file)
@@ -76,6 +76,10 @@ public:
     return _minimum_to_decode(want_to_read, available_chunks, minimum);
   }
 
+  uint64_t get_supported_optimizations() const override {
+    return FLAG_EC_PLUGIN_PARTIAL_READ_OPTIMIZATION;
+  }
+
   unsigned int get_chunk_count() const override {
     return DATA_CHUNKS + CODING_CHUNKS;
   }
index 367a1574a89db12c3344f42703df4a0e8f78fd20..a06f721a20cc24ac8fbdd83b58913597d963b9c2 100644 (file)
@@ -39,6 +39,7 @@ public:
     return 0;
   }
 
+  uint64_t get_supported_optimizations() const override { return 0; }
   unsigned int get_chunk_count() const override { return k + m; }
   unsigned int get_data_chunk_count() const override { return k; }
   unsigned int get_chunk_size(unsigned int object_size) const override {
diff --git a/src/test/erasure-code/TestErasureCodePlugins.cc b/src/test/erasure-code/TestErasureCodePlugins.cc
new file mode 100644 (file)
index 0000000..0efdaa7
--- /dev/null
@@ -0,0 +1,450 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ */
+#include <errno.h>
+#include <stdlib.h>
+#include "arch/probe.h"
+#include "arch/intel.h"
+#include "erasure-code/ErasureCodePlugin.h"
+#include "global/global_context.h"
+#include "common/config_proxy.h"
+#include "gtest/gtest.h"
+using namespace std;
+class PluginTest: public ::testing::TestWithParam<const char *> {
+public:
+  ErasureCodeProfile profile;
+  ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+  ErasureCodeInterfaceRef erasure_code;
+  int chunk_size;
+  
+  PluginTest() {
+    std::stringstream ss(GetParam());
+    while (ss.good()) {
+      std::string keyvalue,k,v;
+      getline(ss, keyvalue, ' ');
+      std::stringstream kv(keyvalue);
+      getline(kv, k, '=');
+      getline(kv, v, '=');
+      profile[k] = v;
+    }
+  }
+  std::string get_plugin() {
+    return profile["plugin"];
+  }
+  void initialize() {
+    EXPECT_FALSE(erasure_code);
+    EXPECT_EQ(0, instance.factory(get_plugin(),
+                                 g_conf().get_val<std::string>("erasure_code_dir"),
+                                 profile,
+                                 &erasure_code,
+                                 &cerr));
+    EXPECT_TRUE(erasure_code.get());
+    chunk_size = erasure_code->get_chunk_size(get_k()*4096);
+  }
+  unsigned int get_k()
+  {
+    return erasure_code->get_data_chunk_count();
+  }
+  unsigned int get_m()
+  {
+    return erasure_code->get_coding_chunk_count();
+  }
+  unsigned int get_k_plus_m()
+  {
+    return erasure_code->get_chunk_count();
+  }
+  void generate_chunk(bufferlist& bl)
+  {
+    ceph::util::random_number_generator<char> random_generator = ceph::util::random_number_generator<char>();
+    ceph::bufferptr b = buffer::create_aligned(chunk_size, 4096);
+    for (int i = 0; i < chunk_size; i++) {
+      b[i] = random_generator();
+    }
+    bl.append(b);
+  }
+  void generate_chunk(bufferlist& bl, char c)
+  {
+    ceph::bufferptr b = buffer::create_aligned(chunk_size, 4096);
+    for (int i = 0; i < chunk_size; i++) {
+      b[i] = c;
+    }
+    bl.append(b);
+  }
+};
+TEST_P(PluginTest,Initialize)
+{
+  initialize();
+}
+TEST_P(PluginTest,PartialRead)
+{
+  initialize();
+  set<int> want_to_encode;
+  for (unsigned int i = 0 ; i < get_k_plus_m(); i++) {
+    want_to_encode.insert(i);
+  }
+  // Test erasure code is systematic and that the data
+  // order is described by get_chunk_mapping().
+  //
+  // Create a buffer and encode it. Compare the
+  // encoded shards of data with the equivalent
+  // range of the buffer.
+  //
+  // If there are no differences the plugin should
+  // report that it supports PARTIAL_READ_OPTIMIZATION
+  bufferlist bl;
+  for (unsigned int i = 0; i < get_k(); i++) {
+    generate_chunk(bl);
+  }
+  map<int,bufferlist> encoded;
+  erasure_code->encode(want_to_encode, bl, &encoded);
+  std::vector<int> chunk_mapping = erasure_code->get_chunk_mapping();
+  bool different = false;
+  for (unsigned int i = 0; i < get_k_plus_m(); i++) {
+    EXPECT_EQ(chunk_size, encoded[i].length());
+    unsigned int index = (chunk_mapping.size() > i) ? chunk_mapping[i] : i;
+    if (i < get_k()) {
+      bufferlist expects;
+      expects.substr_of(bl, i * chunk_size, chunk_size);
+      if (expects != encoded[index]) {
+       different = true;
+      }
+    }
+  }
+  if (erasure_code->get_supported_optimizations() &
+      ErasureCodeInterface::FLAG_EC_PLUGIN_PARTIAL_READ_OPTIMIZATION) {
+    // Plugin should not have PARTIAL_READ_OPTIMIZATION enabled, this
+    // failure proves that it can cause a data integrity issue
+    EXPECT_EQ(different, false);
+  } else {
+    // Very rare chance of a false positive because input buffers are random,
+    // repeatedly hitting this failure means the plugin should be reporting
+    // support for PARTIAL_READ_OPTIMIZAION
+    EXPECT_EQ(different, true);
+  }
+}
+TEST_P(PluginTest,PartialWrite)
+{
+  initialize();
+  set<int> want_to_encode;
+  for (unsigned int i = 0 ; i < get_k_plus_m(); i++) {
+    want_to_encode.insert(i);
+  }
+  // Test erasure code can perform partial writes
+  //
+  // Create buffer 1 that consists of 3 randomly
+  // generated chunks for each shard
+  //
+  // Create buffer 2 that has a different middle
+  // chunk for each shard
+  //
+  // Create buffer 4 that just has the 1 different
+  // middle chunk for each shard
+  //
+  // encoded the 3 buffers. Check if the first and
+  // last chunk of encoded shard buffer 1 and 2 are
+  // the same. Check if the midle chunk of encoded
+  // shard buffer 2 is the same as encoded shard
+  // buffer 3.
+  //
+  // If there are no differences the plugin should
+  // report that it supports PARTIAL_WRITE_OPTIMIZATION
+  bufferlist bl1;
+  bufferlist bl2;
+  bufferlist bl3;
+  for (unsigned int i = 0; i < get_k(); i++) {
+    bufferlist a1,a2,a3,b1,b2,b3,c2;
+    generate_chunk(a1);
+    generate_chunk(a2);
+    generate_chunk(a3);
+    b1 = a1;
+    generate_chunk(b2);
+    b3 = a3;
+    c2 = b2;
+    bl1.append(a1);
+    bl1.append(a2);
+    bl1.append(a3);
+    bl2.append(b1);
+    bl2.append(b2);
+    bl2.append(b3);
+    bl3.append(c2);
+  }
+  map<int,bufferlist> encoded1;
+  erasure_code->encode(want_to_encode, bl1, &encoded1);
+  map<int,bufferlist> encoded2;
+  erasure_code->encode(want_to_encode, bl2, &encoded2);
+  map<int,bufferlist> encoded3;
+  erasure_code->encode(want_to_encode, bl3, &encoded3);
+  bool different = false;
+  for (unsigned int i = 0; i < get_k_plus_m(); i++) {
+    EXPECT_EQ(chunk_size*3, encoded1[i].length());
+    EXPECT_EQ(chunk_size*3, encoded2[i].length());
+    EXPECT_EQ(chunk_size, encoded3[i].length());
+    bufferlist a1,a2,a3,b1,b2,b3,c2;
+    a1.substr_of(encoded1[i],0,chunk_size);
+    a2.substr_of(encoded1[i],chunk_size,chunk_size);
+    a3.substr_of(encoded1[i],chunk_size*2,chunk_size);
+    b1.substr_of(encoded2[i],0,chunk_size);
+    b2.substr_of(encoded2[i],chunk_size,chunk_size);
+    b3.substr_of(encoded2[i],chunk_size*2,chunk_size);
+    c2 = encoded3[i];
+    if ((a1 != b1) || (a3 != b3) || (b2 != c2)) {
+      different = true;
+      std::cout << "plugin " << get_plugin() << " " << profile << " ";
+      if (a1 != b1) {
+       std::cout << "a1!=b1 ";
+      }
+      if (a3 != b3) {
+       std::cout << "a3!=b3 ";
+      }
+      if (b2 != c2) {
+       std::cout << "b2!=c2 ";
+      }
+      std::cout << std::endl;
+    }
+  }
+  if (erasure_code->get_supported_optimizations() &
+      ErasureCodeInterface::FLAG_EC_PLUGIN_PARTIAL_WRITE_OPTIMIZATION) {
+    // Plugin should not have PARTIAL_WRITE_OPTIMIZATION enabled, this
+    // failure proves that it can cause a data integrity issue
+    EXPECT_EQ(different, false);
+  } else {
+    // Very rare chance of a false positive because input buffers are random,
+    // repeatedly hitting this failure means the plugin should be reporting
+    // support for PARTIAL_WRITE_OPTIMIZAION
+    EXPECT_EQ(different, true);
+  }
+}
+TEST_P(PluginTest,ZeroInZeroOut)
+{
+  initialize();
+  set<int> want_to_encode;
+  for (unsigned int i = 0 ; i < get_k_plus_m(); i++) {
+    want_to_encode.insert(i);
+  }
+  // Test erasure code generates zeros for coding parity if data chunks are zeros
+  //
+  // Create a buffer of all zeros and encode it, test if all the data and parity
+  // chunks are all zeros.
+  //
+  // If there are no differences the plugin should
+  // report that it supports ZERO_INPUT_ZERO_OUTPUT_OPTIMIZATION
+  bufferlist bl;
+  for (unsigned int i = 0; i < get_k(); i++) {
+    generate_chunk(bl, 0);
+  }
+  map<int,bufferlist> encoded;
+  erasure_code->encode(want_to_encode, bl, &encoded);
+  bool different = false;
+  bufferlist expects;
+  generate_chunk(expects, 0);
+  for (unsigned int i = 0; i < get_k_plus_m(); i++) {
+    EXPECT_EQ(chunk_size, encoded[i].length());
+    if (expects != encoded[i]) {
+      different = true;
+    }
+  }
+  if (erasure_code->get_supported_optimizations() &
+      ErasureCodeInterface::FLAG_EC_PLUGIN_ZERO_INPUT_ZERO_OUTPUT_OPTIMIZATION) {
+    // Plugin should not have ZERO_INPUT_ZERO_OUTPUT_OPTIMIZATION enabled, this
+    // failure proves that it can cause a data integrity issue
+    EXPECT_EQ(different, false);
+  } else {
+    // Plugin should be supporting ZERO_INPUT_ZERO_OUTPUT_OPTIMIZATION
+    GTEST_SKIP() << "ZERO_INPUT_ZERO_OUTPUT_OPTIMIZATION not supported"
+      " but test indicates support is possible for this configuration";
+  }
+}
+INSTANTIATE_TEST_SUITE_P(
+  PluginTests,
+  PluginTest,
+  ::testing::Values(
+    "plugin=isa technique=reed_sol_van k=2 m=1",
+    "plugin=isa technique=reed_sol_van k=3 m=1",
+    "plugin=isa technique=reed_sol_van k=4 m=1",
+    "plugin=isa technique=reed_sol_van k=5 m=1",
+    "plugin=isa technique=reed_sol_van k=6 m=1",
+    "plugin=isa technique=reed_sol_van k=2 m=2",
+    "plugin=isa technique=reed_sol_van k=3 m=2",
+    "plugin=isa technique=reed_sol_van k=4 m=2",
+    "plugin=isa technique=reed_sol_van k=5 m=2",
+    "plugin=isa technique=reed_sol_van k=6 m=2",
+    "plugin=isa technique=reed_sol_van k=2 m=3",
+    "plugin=isa technique=reed_sol_van k=3 m=3",
+    "plugin=isa technique=reed_sol_van k=4 m=3",
+    "plugin=isa technique=reed_sol_van k=5 m=3",
+    "plugin=isa technique=reed_sol_van k=6 m=3",
+    "plugin=isa technique=cauchy k=2 m=1",
+    "plugin=isa technique=cauchy k=3 m=1",
+    "plugin=isa technique=cauchy k=4 m=1",
+    "plugin=isa technique=cauchy k=5 m=1",
+    "plugin=isa technique=cauchy k=6 m=1",
+    "plugin=isa technique=cauchy k=2 m=2",
+    "plugin=isa technique=cauchy k=3 m=2",
+    "plugin=isa technique=cauchy k=4 m=2",
+    "plugin=isa technique=cauchy k=5 m=2",
+    "plugin=isa technique=cauchy k=6 m=2",
+    "plugin=isa technique=cauchy k=2 m=3",
+    "plugin=isa technique=cauchy k=3 m=3",
+    "plugin=isa technique=cauchy k=4 m=3",
+    "plugin=isa technique=cauchy k=5 m=3",
+    "plugin=isa technique=cauchy k=6 m=3",
+    "plugin=jerasure technique=reed_sol_van k=2 m=1",
+    "plugin=jerasure technique=reed_sol_van k=3 m=1",
+    "plugin=jerasure technique=reed_sol_van k=4 m=1",
+    "plugin=jerasure technique=reed_sol_van k=5 m=1",
+    "plugin=jerasure technique=reed_sol_van k=6 m=1",
+    "plugin=jerasure technique=reed_sol_van k=2 m=2",
+    "plugin=jerasure technique=reed_sol_van k=3 m=2",
+    "plugin=jerasure technique=reed_sol_van k=4 m=2",
+    "plugin=jerasure technique=reed_sol_van k=5 m=2",
+    "plugin=jerasure technique=reed_sol_van k=6 m=2",
+    "plugin=jerasure technique=reed_sol_van k=2 m=3",
+    "plugin=jerasure technique=reed_sol_van k=3 m=3",
+    "plugin=jerasure technique=reed_sol_van k=4 m=3",
+    "plugin=jerasure technique=reed_sol_van k=5 m=3",
+    "plugin=jerasure technique=reed_sol_van k=6 m=3",
+    "plugin=jerasure technique=reed_sol_r6_op k=2 m=2",
+    "plugin=jerasure technique=reed_sol_r6_op k=3 m=2",
+    "plugin=jerasure technique=reed_sol_r6_op k=4 m=2",
+    "plugin=jerasure technique=reed_sol_r6_op k=5 m=2",
+    "plugin=jerasure technique=reed_sol_r6_op k=6 m=2",
+    "plugin=jerasure technique=cauchy_orig k=2 m=1 packetsize=32",
+    "plugin=jerasure technique=cauchy_orig k=3 m=1 packetsize=32",
+    "plugin=jerasure technique=cauchy_orig k=4 m=1 packetsize=32",
+    "plugin=jerasure technique=cauchy_orig k=5 m=1 packetsize=32",
+    "plugin=jerasure technique=cauchy_orig k=6 m=1 packetsize=32",
+    "plugin=jerasure technique=cauchy_orig k=2 m=2 packetsize=32",
+    "plugin=jerasure technique=cauchy_orig k=3 m=2 packetsize=32",
+    "plugin=jerasure technique=cauchy_orig k=4 m=2 packetsize=32",
+    "plugin=jerasure technique=cauchy_orig k=5 m=2 packetsize=32",
+    "plugin=jerasure technique=cauchy_orig k=6 m=2 packetsize=32",
+    "plugin=jerasure technique=cauchy_orig k=2 m=3 packetsize=32",
+    "plugin=jerasure technique=cauchy_orig k=3 m=3 packetsize=32",
+    "plugin=jerasure technique=cauchy_orig k=4 m=3 packetsize=32",
+    "plugin=jerasure technique=cauchy_orig k=5 m=3 packetsize=32",
+    "plugin=jerasure technique=cauchy_orig k=6 m=3 packetsize=32",
+    "plugin=jerasure technique=cauchy_good k=2 m=1 packetsize=32",
+    "plugin=jerasure technique=cauchy_good k=3 m=1 packetsize=32",
+    "plugin=jerasure technique=cauchy_good k=4 m=1 packetsize=32",
+    "plugin=jerasure technique=cauchy_good k=5 m=1 packetsize=32",
+    "plugin=jerasure technique=cauchy_good k=6 m=1 packetsize=32",
+    "plugin=jerasure technique=cauchy_good k=2 m=2 packetsize=32",
+    "plugin=jerasure technique=cauchy_good k=3 m=2 packetsize=32",
+    "plugin=jerasure technique=cauchy_good k=4 m=2 packetsize=32",
+    "plugin=jerasure technique=cauchy_good k=5 m=2 packetsize=32",
+    "plugin=jerasure technique=cauchy_good k=6 m=2 packetsize=32",
+    "plugin=jerasure technique=cauchy_good k=2 m=3 packetsize=32",
+    "plugin=jerasure technique=cauchy_good k=3 m=3 packetsize=32",
+    "plugin=jerasure technique=cauchy_good k=4 m=3 packetsize=32",
+    "plugin=jerasure technique=cauchy_good k=5 m=3 packetsize=32",
+    "plugin=jerasure technique=cauchy_good k=6 m=3 packetsize=32",
+    "plugin=jerasure technique=liberation k=2 m=1 packetsize=32",
+    "plugin=jerasure technique=liberation k=3 m=1 packetsize=32",
+    "plugin=jerasure technique=liberation k=4 m=1 packetsize=32",
+    "plugin=jerasure technique=liberation k=5 m=1 packetsize=32",
+    "plugin=jerasure technique=liberation k=6 m=1 packetsize=32",
+    "plugin=jerasure technique=liberation k=2 m=2 packetsize=32",
+    "plugin=jerasure technique=liberation k=3 m=2 packetsize=32",
+    "plugin=jerasure technique=liberation k=4 m=2 packetsize=32",
+    "plugin=jerasure technique=liberation k=5 m=2 packetsize=32",
+    "plugin=jerasure technique=liberation k=6 m=2 packetsize=32",
+    "plugin=jerasure technique=blaum_roth k=2 m=1 packetsize=32",
+    "plugin=jerasure technique=blaum_roth k=3 m=1 packetsize=32",
+    "plugin=jerasure technique=blaum_roth k=4 m=1 packetsize=32",
+    "plugin=jerasure technique=blaum_roth k=5 m=1 packetsize=32",
+    "plugin=jerasure technique=blaum_roth k=6 m=1 packetsize=32",
+    "plugin=jerasure technique=blaum_roth k=2 m=2 packetsize=32",
+    "plugin=jerasure technique=blaum_roth k=3 m=2 packetsize=32",
+    "plugin=jerasure technique=blaum_roth k=4 m=2 packetsize=32",
+    "plugin=jerasure technique=blaum_roth k=5 m=2 packetsize=32",
+    "plugin=jerasure technique=blaum_roth k=6 m=2 packetsize=32",
+    "plugin=jerasure technique=liber8tion k=2 m=2 packetsize=32",
+    "plugin=jerasure technique=liber8tion k=3 m=2 packetsize=32",
+    "plugin=jerasure technique=liber8tion k=4 m=2 packetsize=32",
+    "plugin=jerasure technique=liber8tion k=5 m=2 packetsize=32",
+    "plugin=jerasure technique=liber8tion k=6 m=2 packetsize=32",
+    "plugin=clay k=2 m=1",
+    "plugin=clay k=3 m=1",
+    "plugin=clay k=4 m=1",
+    "plugin=clay k=5 m=1",
+    "plugin=clay k=6 m=1",
+    "plugin=clay k=2 m=2",
+    "plugin=clay k=3 m=2",
+    "plugin=clay k=4 m=2",
+    "plugin=clay k=5 m=2",
+    "plugin=clay k=6 m=2",
+    "plugin=clay k=2 m=3",
+    "plugin=clay k=3 m=3",
+    "plugin=clay k=4 m=3",
+    "plugin=clay k=5 m=3",
+    "plugin=clay k=6 m=3",
+    "plugin=shec technique=single k=2 m=1 c=1",
+    "plugin=shec technique=single k=3 m=1 c=1",
+    "plugin=shec technique=single k=4 m=1 c=1",
+    "plugin=shec technique=single k=5 m=1 c=1",
+    "plugin=shec technique=single k=6 m=1 c=1",
+    "plugin=shec technique=single k=2 m=2 c=1",
+    "plugin=shec technique=single k=3 m=2 c=1",
+    "plugin=shec technique=single k=4 m=2 c=1",
+    "plugin=shec technique=single k=5 m=2 c=1",
+    "plugin=shec technique=single k=6 m=2 c=1",
+    "plugin=shec technique=single k=3 m=3 c=1",
+    "plugin=shec technique=single k=4 m=3 c=1",
+    "plugin=shec technique=single k=5 m=3 c=1",
+    "plugin=shec technique=single k=6 m=3 c=1",
+    "plugin=shec technique=single k=3 m=3 c=2",
+    "plugin=shec technique=single k=4 m=3 c=2",
+    "plugin=shec technique=single k=5 m=3 c=2",
+    "plugin=shec technique=single k=6 m=3 c=2",
+    "plugin=shec technique=multiple k=2 m=1 c=1",
+    "plugin=shec technique=multiple k=3 m=1 c=1",
+    "plugin=shec technique=multiple k=4 m=1 c=1",
+    "plugin=shec technique=multiple k=5 m=1 c=1",
+    "plugin=shec technique=multiple k=6 m=1 c=1",
+    "plugin=shec technique=multiple k=2 m=2 c=1",
+    "plugin=shec technique=multiple k=3 m=2 c=1",
+    "plugin=shec technique=multiple k=4 m=2 c=1",
+    "plugin=shec technique=multiple k=5 m=2 c=1",
+    "plugin=shec technique=multiple k=6 m=2 c=1",
+    "plugin=shec technique=multiple k=3 m=3 c=1",
+    "plugin=shec technique=multiple k=4 m=3 c=1",
+    "plugin=shec technique=multiple k=5 m=3 c=1",
+    "plugin=shec technique=multiple k=6 m=3 c=1",
+    "plugin=shec technique=multiple k=3 m=3 c=2",
+    "plugin=shec technique=multiple k=4 m=3 c=2",
+    "plugin=shec technique=multiple k=5 m=3 c=2",
+    "plugin=shec technique=multiple k=6 m=3 c=2",
+    "plugin=lrc mapping=_DD layers=[[\"cDD\",\"\"]]",
+    "plugin=lrc mapping=_DDD layers=[[\"cDDD\",\"\"]]",
+    "plugin=lrc mapping=_DDDD layers=[[\"cDDDD\",\"\"]]",
+    "plugin=lrc mapping=_DDDDD layers=[[\"cDDDDD\",\"\"]]",
+    "plugin=lrc mapping=_DDDDDD layers=[[\"cDDDDDD\",\"\"]]",
+    "plugin=lrc mapping=_D_D layers=[[\"cDcD\",\"\"]]",
+    "plugin=lrc mapping=_D_DD layers=[[\"cDcDD\",\"\"]]",
+    "plugin=lrc mapping=_D_DDD layers=[[\"cDcDDD\",\"\"]]",
+    "plugin=lrc mapping=_D_DDDD layers=[[\"cDcDDDD\",\"\"]]",
+    "plugin=lrc mapping=_D_DDDDD layers=[[\"cDcDDDDD\",\"\"]]",
+    "plugin=lrc mapping=_D_D_ layers=[[\"cDcDc\",\"\"]]",
+    "plugin=lrc mapping=_D_D_D layers=[[\"cDcDcD\",\"\"]]",
+    "plugin=lrc mapping=_D_D_DD layers=[[\"cDcDcDD\",\"\"]]",
+    "plugin=lrc mapping=_D_D_DDD layers=[[\"cDcDcDDD\",\"\"]]",
+    "plugin=lrc mapping=_D_D_DDDD layers=[[\"cDcDcDDDD\",\"\"]]",
+    "plugin=jerasure technique=reed_sol_van k=6 m=3 w=16",
+    "plugin=jerasure technique=reed_sol_van k=6 m=3 w=32"
+    "plugin=jerasure technique=liberation k=6 m=2 packetsize=32 w=11",
+    "plugin=jerasure technique=liberation k=6 m=2 packetsize=36 w=13",
+    "plugin=jerasure technique=blaum_roth k=6 m=2 packetsize=44 w=7",
+    "plugin=jerasure technique=blaum_roth k=6 m=2 packetsize=60 w=10",
+    "plugin=jerasure technique=liber8tion k=2 m=2 packetsize=92"
+  )
+);
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; ninja &&
+ *   ninja unittest_erasure_code_plugins &&
+ *   valgrind --tool=memcheck ./unittest_erasure_code_plugins \
+ *      --gtest_filter=*.* --log-to-stderr=true --debug-osd=20"
+ * End:
+ */
\ No newline at end of file