]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
erasure-code: add ErasureCode::encode unit test
authorLoic Dachary <loic-201408@dachary.org>
Mon, 13 Oct 2014 12:48:27 +0000 (14:48 +0200)
committerLoic Dachary <loic-201408@dachary.org>
Tue, 21 Oct 2014 17:33:51 +0000 (10:33 -0700)
Re-create and describe the situation that is fixed by
91a7e18f60bbc9acab3045baaa1b6505474ec4a9 which reworks the buffer
preparation function provided by ErasureCode::encode.

http://tracker.ceph.com/issues/9408 Refs: #9408

Signed-off-by: Loic Dachary <loic-201408@dachary.org>
src/test/erasure-code/Makefile.am
src/test/erasure-code/TestErasureCode.cc [new file with mode: 0644]

index 6cef35958e7102d8d9a5e53c1f637ccdfaf5494c..5b5aaf895baf596ef8568afcad045f197d9b0b3c 100644 (file)
@@ -98,6 +98,13 @@ unittest_erasure_code_plugin_LDADD += -ldl
 endif
 check_PROGRAMS += unittest_erasure_code_plugin
 
+unittest_erasure_code_SOURCES = \
+       erasure-code/ErasureCode.cc \
+       test/erasure-code/TestErasureCode.cc
+unittest_erasure_code_CXXFLAGS = $(UNITTEST_CXXFLAGS)
+unittest_erasure_code_LDADD = $(LIBOSD) $(LIBCOMMON) $(UNITTEST_LDADD) $(CEPH_GLOBAL)
+check_PROGRAMS += unittest_erasure_code
+
 unittest_erasure_code_jerasure_SOURCES = \
        test/erasure-code/TestErasureCodeJerasure.cc \
        ${jerasure_sources}
diff --git a/src/test/erasure-code/TestErasureCode.cc b/src/test/erasure-code/TestErasureCode.cc
new file mode 100644 (file)
index 0000000..c572cdc
--- /dev/null
@@ -0,0 +1,134 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph distributed storage system
+ *
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <errno.h>
+
+#include "global/global_init.h"
+#include "erasure-code/ErasureCode.h"
+#include "common/ceph_argparse.h"
+#include "global/global_context.h"
+#include "gtest/gtest.h"
+
+class ErasureCodeTest : public ErasureCode {
+public:
+  map<int, bufferlist> encode_chunks_encoded;
+  unsigned int k;
+  unsigned int m;
+  unsigned int chunk_size;
+
+  ErasureCodeTest(unsigned int _k, unsigned int _m, unsigned int _chunk_size) :
+    k(_k), m(_m), chunk_size(_chunk_size) {}
+  virtual ~ErasureCodeTest() {}
+
+  virtual unsigned int get_chunk_count() const { return k + m; }
+  virtual unsigned int get_data_chunk_count() const { return k; }
+  virtual unsigned int get_chunk_size(unsigned int object_size) const {
+    return chunk_size;
+  }
+  virtual int encode_chunks(const set<int> &want_to_encode,
+                           map<int, bufferlist> *encoded) {
+    encode_chunks_encoded = *encoded;
+    return 0;
+  }
+  virtual int create_ruleset(const string &name,
+                            CrushWrapper &crush,
+                            ostream *ss) const { return 0; }
+
+};
+
+/*
+ *  If we have a buffer of 5 bytes (X below) and a chunk size of 3
+ *  bytes, for k=3, m=1 an additional 7 bytes (P and C below) will
+ *  need to be allocated for padding (P) and the 3 coding bytes (C).
+ *
+ *  X -+ +----------+ +-X
+ *  X  | | data   0 | | X
+ *  X  | +----------+ | X
+ *  X  | +----------+ | X -> +-X
+ *  X -+ | data   1 | +-X -> | X
+ *  P -+ +----------+        | P
+ *  P  | +----------+        | P
+ *  P  | | data   2 |        | P
+ *  P  | +----------+        | P
+ *  C  | +----------+        | C
+ *  C  | | coding 3 |        | C
+ *  C -+ +----------+        +-C
+ *
+ *  The data chunks 1 and 2 (data 1 and data 2 above) overflow the
+ *  original buffer because it needs padding. A new buffer will
+ *  be allocated to contain the chunk that overflows and all other
+ *  chunks after it, including the coding chunk(s).
+ *
+ *  The following test creates a siguation where the buffer provided
+ *  for encoding is not memory aligned. After encoding it asserts that:
+ *
+ *   a) each chunk is SIMD aligned
+ *   b) the data 1 chunk content is as expected which implies that its
+ *      content has been copied over.
+ *
+ *  It is possible for a flawed implementation to pas the test because the
+ *  underlying allocation function enforces it.
+ */
+TEST(ErasureCodeTest, encode_memory_align)
+{
+  int k = 3;
+  int m = 1;
+  unsigned chunk_size = ErasureCode::SIMD_ALIGN * 7;
+  ErasureCodeTest erasure_code(k, m, chunk_size);
+
+  set<int> want_to_encode;
+  for (unsigned int i = 0; i < erasure_code.get_chunk_count(); i++)
+    want_to_encode.insert(i);
+  string data(chunk_size + chunk_size / 2, 'X'); // uses 1.5 chunks out of 3
+  // make sure nothing is memory aligned
+  bufferptr ptr(buffer::create_aligned(data.length() + 1, ErasureCode::SIMD_ALIGN));
+  ptr.copy_in(1, data.length(), data.c_str());
+  ptr.set_offset(1);
+  ptr.set_length(data.length());
+  bufferlist in;
+  in.append(ptr);
+  map<int, bufferlist> encoded;
+
+  ASSERT_FALSE(in.is_aligned(ErasureCode::SIMD_ALIGN));
+  ASSERT_EQ(0, erasure_code.encode(want_to_encode, in, &encoded));
+  for (unsigned int i = 0; i < erasure_code.get_chunk_count(); i++)
+    ASSERT_TRUE(encoded[i].is_aligned(ErasureCode::SIMD_ALIGN));
+  for (unsigned i = 0; i < chunk_size / 2; i++)
+    ASSERT_EQ(encoded[1][i], 'X');
+  ASSERT_NE(encoded[1][chunk_size / 2], 'X');
+}
+
+int main(int argc, char **argv)
+{
+  vector<const char*> args;
+  argv_to_vec(argc, (const char **)argv, args);
+
+  global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0);
+  common_init_finish(g_ceph_context);
+
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ;
+ *   make -j4 unittest_erasure_code &&
+ *   valgrind --tool=memcheck --leak-check=full \
+ *      ./unittest_erasure_code \
+ *      --gtest_filter=*.* --log-to-stderr=true"
+ * End:
+ */