diff options
Diffstat (limited to '')
-rw-r--r-- | src/test/erasure-code/TestErasureCode.cc | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/src/test/erasure-code/TestErasureCode.cc b/src/test/erasure-code/TestErasureCode.cc new file mode 100644 index 000000000..98d5bb2ed --- /dev/null +++ b/src/test/erasure-code/TestErasureCode.cc @@ -0,0 +1,167 @@ +// -*- 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 <stdlib.h> + +#include "erasure-code/ErasureCode.h" +#include "global/global_context.h" +#include "common/config.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) {} + ~ErasureCodeTest() override {} + + int init(ErasureCodeProfile &profile, ostream *ss) 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 { + return chunk_size; + } + int encode_chunks(const set<int> &want_to_encode, + map<int, bufferlist> *encoded) override { + encode_chunks_encoded = *encoded; + return 0; + } + int decode_chunks(const set<int> &want_to_read, + const map<int, bufferlist> &chunks, + map<int, bufferlist> *decoded) override { + ceph_abort_msg("ErasureCode::decode_chunks not implemented"); + } + + int create_rule(const string &name, + CrushWrapper &crush, + ostream *ss) const override { 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'); +} + +TEST(ErasureCodeTest, encode_misaligned_non_contiguous) +{ + 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, 'X'); + // create a non contiguous bufferlist where the frist and the second + // bufferptr are not size aligned although they are memory aligned + bufferlist in; + { + bufferptr ptr(buffer::create_aligned(data.length() - 1, ErasureCode::SIMD_ALIGN)); + in.append(ptr); + } + { + bufferptr ptr(buffer::create_aligned(data.length() + 1, ErasureCode::SIMD_ALIGN)); + in.append(ptr); + } + map<int, bufferlist> encoded; + + ASSERT_FALSE(in.is_contiguous()); + ASSERT_TRUE(in.front().is_aligned(ErasureCode::SIMD_ALIGN)); + ASSERT_FALSE(in.front().is_n_align_sized(chunk_size)); + ASSERT_TRUE(in.back().is_aligned(ErasureCode::SIMD_ALIGN)); + ASSERT_FALSE(in.back().is_n_align_sized(chunk_size)); + 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)); + ASSERT_TRUE(encoded[i].is_n_align_sized(chunk_size)); + } +} + +/* + * 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: + */ |