diff options
Diffstat (limited to '')
-rw-r--r-- | src/test/msgr/test_frames_v2.cc | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/src/test/msgr/test_frames_v2.cc b/src/test/msgr/test_frames_v2.cc new file mode 100644 index 00000000..5958af23 --- /dev/null +++ b/src/test/msgr/test_frames_v2.cc @@ -0,0 +1,450 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2020 Red Hat + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "msg/async/frames_v2.h" + +#include <numeric> +#include <ostream> +#include <string> +#include <tuple> + +#include "auth/Auth.h" +#include "common/ceph_argparse.h" +#include "global/global_init.h" +#include "global/global_context.h" +#include "include/Context.h" + +#include <gtest/gtest.h> + +namespace ceph::msgr::v2 { + +// MessageFrame with the first segment not fixed to ceph_msg_header2 +struct TestFrame : Frame<TestFrame, + /* four segments */ + segment_t::DEFAULT_ALIGNMENT, + segment_t::DEFAULT_ALIGNMENT, + segment_t::DEFAULT_ALIGNMENT, + segment_t::PAGE_SIZE_ALIGNMENT> { + static constexpr Tag tag = static_cast<Tag>(123); + + static TestFrame Encode(const bufferlist& header, + const bufferlist& front, + const bufferlist& middle, + const bufferlist& data) { + TestFrame f; + f.segments[SegmentIndex::Msg::HEADER] = header; + f.segments[SegmentIndex::Msg::FRONT] = front; + f.segments[SegmentIndex::Msg::MIDDLE] = middle; + f.segments[SegmentIndex::Msg::DATA] = data; + + // discard cached crcs for perf tests + f.segments[SegmentIndex::Msg::HEADER].invalidate_crc(); + f.segments[SegmentIndex::Msg::FRONT].invalidate_crc(); + f.segments[SegmentIndex::Msg::MIDDLE].invalidate_crc(); + f.segments[SegmentIndex::Msg::DATA].invalidate_crc(); + return f; + } + + static TestFrame Decode(segment_bls_t& segment_bls) { + TestFrame f; + // Transfer segments' bufferlists. If segment_bls contains + // less than SegmentsNumV segments, the missing ones will be + // seen as empty. + for (size_t i = 0; i < segment_bls.size(); i++) { + f.segments[i] = std::move(segment_bls[i]); + } + return f; + } + + bufferlist& header() { + return segments[SegmentIndex::Msg::HEADER]; + } + bufferlist& front() { + return segments[SegmentIndex::Msg::FRONT]; + } + bufferlist& middle() { + return segments[SegmentIndex::Msg::MIDDLE]; + } + bufferlist& data() { + return segments[SegmentIndex::Msg::DATA]; + } + +protected: + using Frame::Frame; +}; + +struct mode_t { + bool is_rev1; + bool is_secure; +}; + +static std::ostream& operator<<(std::ostream& os, const mode_t& m) { + os << "msgr2." << (m.is_rev1 ? "1" : "0") + << (m.is_secure ? "-secure" : "-crc"); + return os; +} + +static const mode_t modes[] = { + {false, false}, + {false, true}, + {true, false}, + {true, true}, +}; + +struct round_trip_instance_t { + uint32_t header_len; + uint32_t front_len; + uint32_t middle_len; + uint32_t data_len; + + // expected number of segments (same for each mode) + size_t num_segments; + // expected layout (different for each mode) + uint32_t onwire_lens[4][MAX_NUM_SEGMENTS + 2]; +}; + +static std::ostream& operator<<(std::ostream& os, + const round_trip_instance_t& rti) { + os << rti.header_len << "+" << rti.front_len << "+" + << rti.middle_len << "+" << rti.data_len; + return os; +} + +static bufferlist make_bufferlist(size_t len, char c) { + bufferlist bl; + if (len > 0) { + bl.reserve(len); + bl.append(std::string(len, c)); + } + return bl; +} + +bool disassemble_frame(FrameAssembler& frame_asm, bufferlist& frame_bl, + Tag& tag, segment_bls_t& segment_bls) { + bufferlist preamble_bl; + frame_bl.splice(0, frame_asm.get_preamble_onwire_len(), &preamble_bl); + tag = frame_asm.disassemble_preamble(preamble_bl); + + do { + size_t seg_idx = segment_bls.size(); + segment_bls.emplace_back(); + + uint32_t onwire_len = frame_asm.get_segment_onwire_len(seg_idx); + if (onwire_len > 0) { + frame_bl.splice(0, onwire_len, &segment_bls.back()); + } + } while (segment_bls.size() < frame_asm.get_num_segments()); + + bufferlist epilogue_bl; + uint32_t epilogue_onwire_len = frame_asm.get_epilogue_onwire_len(); + if (epilogue_onwire_len > 0) { + frame_bl.splice(0, epilogue_onwire_len, &epilogue_bl); + } + frame_asm.disassemble_first_segment(preamble_bl, segment_bls[0]); + return frame_asm.disassemble_remaining_segments(segment_bls.data(), + epilogue_bl); +} + +class RoundTripTestBase : public ::testing::TestWithParam< + std::tuple<round_trip_instance_t, mode_t>> { +protected: + RoundTripTestBase() + : m_tx_frame_asm(&m_tx_crypto, std::get<1>(GetParam()).is_rev1), + m_rx_frame_asm(&m_rx_crypto, std::get<1>(GetParam()).is_rev1), + m_header(make_bufferlist(std::get<0>(GetParam()).header_len, 'H')), + m_front(make_bufferlist(std::get<0>(GetParam()).front_len, 'F')), + m_middle(make_bufferlist(std::get<0>(GetParam()).middle_len, 'M')), + m_data(make_bufferlist(std::get<0>(GetParam()).data_len, 'D')) { + const auto& m = std::get<1>(GetParam()); + if (m.is_secure) { + AuthConnectionMeta auth_meta; + auth_meta.con_mode = CEPH_CON_MODE_SECURE; + // see AuthConnectionMeta::get_connection_secret_length() + auth_meta.connection_secret.resize(64); + g_ceph_context->random()->get_bytes(auth_meta.connection_secret.data(), + auth_meta.connection_secret.size()); + m_tx_crypto = ceph::crypto::onwire::rxtx_t::create_handler_pair( + g_ceph_context, auth_meta, /*new_nonce_format=*/m.is_rev1, + /*crossed=*/false); + m_rx_crypto = ceph::crypto::onwire::rxtx_t::create_handler_pair( + g_ceph_context, auth_meta, /*new_nonce_format=*/m.is_rev1, + /*crossed=*/true); + } + } + + void check_frame_assembler(const FrameAssembler& frame_asm) { + const auto& [rti, m] = GetParam(); + const auto& onwire_lens = rti.onwire_lens[m.is_rev1 << 1 | m.is_secure]; + EXPECT_EQ(rti.header_len + rti.front_len + rti.middle_len + rti.data_len, + frame_asm.get_frame_logical_len()); + ASSERT_EQ(rti.num_segments, frame_asm.get_num_segments()); + EXPECT_EQ(onwire_lens[0], frame_asm.get_preamble_onwire_len()); + for (size_t i = 0; i < rti.num_segments; i++) { + EXPECT_EQ(onwire_lens[i + 1], frame_asm.get_segment_onwire_len(i)); + } + EXPECT_EQ(onwire_lens[rti.num_segments + 1], + frame_asm.get_epilogue_onwire_len()); + EXPECT_EQ(std::accumulate(std::begin(onwire_lens), std::end(onwire_lens), + uint64_t(0)), + frame_asm.get_frame_onwire_len()); + } + + void test_round_trip() { + auto tx_frame = TestFrame::Encode(m_header, m_front, m_middle, m_data); + auto onwire_bl = tx_frame.get_buffer(m_tx_frame_asm); + check_frame_assembler(m_tx_frame_asm); + EXPECT_EQ(m_tx_frame_asm.get_frame_onwire_len(), onwire_bl.length()); + + Tag rx_tag; + segment_bls_t rx_segment_bls; + EXPECT_TRUE(disassemble_frame(m_rx_frame_asm, onwire_bl, rx_tag, + rx_segment_bls)); + check_frame_assembler(m_rx_frame_asm); + EXPECT_EQ(0, onwire_bl.length()); + EXPECT_EQ(TestFrame::tag, rx_tag); + EXPECT_EQ(m_rx_frame_asm.get_num_segments(), rx_segment_bls.size()); + + auto rx_frame = TestFrame::Decode(rx_segment_bls); + EXPECT_TRUE(m_header.contents_equal(rx_frame.header())); + EXPECT_TRUE(m_front.contents_equal(rx_frame.front())); + EXPECT_TRUE(m_middle.contents_equal(rx_frame.middle())); + EXPECT_TRUE(m_data.contents_equal(rx_frame.data())); + } + + ceph::crypto::onwire::rxtx_t m_tx_crypto; + ceph::crypto::onwire::rxtx_t m_rx_crypto; + FrameAssembler m_tx_frame_asm; + FrameAssembler m_rx_frame_asm; + + const bufferlist m_header; + const bufferlist m_front; + const bufferlist m_middle; + const bufferlist m_data; +}; + +class RoundTripTest : public RoundTripTestBase {}; + +TEST_P(RoundTripTest, Basic) { + test_round_trip(); +} + +TEST_P(RoundTripTest, Reuse) { + for (int i = 0; i < 3; i++) { + test_round_trip(); + } +} + +static const round_trip_instance_t round_trip_instances[] = { + // first segment is empty + { 0, 0, 0, 0, 1, {{32, 0, 17, 0, 0, 0}, + {32, 0, 32, 0, 0, 0}, + {32, 0, 0, 0, 0, 0}, + {96, 0, 0, 0, 0, 0}}}, + { 0, 0, 0, 303, 4, {{32, 0, 0, 0, 303, 17}, + {32, 0, 0, 0, 304, 32}, + {32, 0, 0, 0, 303, 13}, + {96, 0, 0, 0, 304, 32}}}, + { 0, 0, 202, 0, 3, {{32, 0, 0, 202, 17, 0}, + {32, 0, 0, 208, 32, 0}, + {32, 0, 0, 202, 13, 0}, + {96, 0, 0, 208, 32, 0}}}, + { 0, 0, 202, 303, 4, {{32, 0, 0, 202, 303, 17}, + {32, 0, 0, 208, 304, 32}, + {32, 0, 0, 202, 303, 13}, + {96, 0, 0, 208, 304, 32}}}, + { 0, 101, 0, 0, 2, {{32, 0, 101, 17, 0, 0}, + {32, 0, 112, 32, 0, 0}, + {32, 0, 101, 13, 0, 0}, + {96, 0, 112, 32, 0, 0}}}, + { 0, 101, 0, 303, 4, {{32, 0, 101, 0, 303, 17}, + {32, 0, 112, 0, 304, 32}, + {32, 0, 101, 0, 303, 13}, + {96, 0, 112, 0, 304, 32}}}, + { 0, 101, 202, 0, 3, {{32, 0, 101, 202, 17, 0}, + {32, 0, 112, 208, 32, 0}, + {32, 0, 101, 202, 13, 0}, + {96, 0, 112, 208, 32, 0}}}, + { 0, 101, 202, 303, 4, {{32, 0, 101, 202, 303, 17}, + {32, 0, 112, 208, 304, 32}, + {32, 0, 101, 202, 303, 13}, + {96, 0, 112, 208, 304, 32}}}, + + // first segment is fully inlined, inline buffer is not full + { 1, 0, 0, 0, 1, {{32, 1, 17, 0, 0, 0}, + {32, 16, 32, 0, 0, 0}, + {32, 5, 0, 0, 0, 0}, + {96, 0, 0, 0, 0, 0}}}, + { 1, 0, 0, 303, 4, {{32, 1, 0, 0, 303, 17}, + {32, 16, 0, 0, 304, 32}, + {32, 5, 0, 0, 303, 13}, + {96, 0, 0, 0, 304, 32}}}, + { 1, 0, 202, 0, 3, {{32, 1, 0, 202, 17, 0}, + {32, 16, 0, 208, 32, 0}, + {32, 5, 0, 202, 13, 0}, + {96, 0, 0, 208, 32, 0}}}, + { 1, 0, 202, 303, 4, {{32, 1, 0, 202, 303, 17}, + {32, 16, 0, 208, 304, 32}, + {32, 5, 0, 202, 303, 13}, + {96, 0, 0, 208, 304, 32}}}, + { 1, 101, 0, 0, 2, {{32, 1, 101, 17, 0, 0}, + {32, 16, 112, 32, 0, 0}, + {32, 5, 101, 13, 0, 0}, + {96, 0, 112, 32, 0, 0}}}, + { 1, 101, 0, 303, 4, {{32, 1, 101, 0, 303, 17}, + {32, 16, 112, 0, 304, 32}, + {32, 5, 101, 0, 303, 13}, + {96, 0, 112, 0, 304, 32}}}, + { 1, 101, 202, 0, 3, {{32, 1, 101, 202, 17, 0}, + {32, 16, 112, 208, 32, 0}, + {32, 5, 101, 202, 13, 0}, + {96, 0, 112, 208, 32, 0}}}, + { 1, 101, 202, 303, 4, {{32, 1, 101, 202, 303, 17}, + {32, 16, 112, 208, 304, 32}, + {32, 5, 101, 202, 303, 13}, + {96, 0, 112, 208, 304, 32}}}, + + // first segment is fully inlined, inline buffer is full + {48, 0, 0, 0, 1, {{32, 48, 17, 0, 0, 0}, + {32, 48, 32, 0, 0, 0}, + {32, 52, 0, 0, 0, 0}, + {96, 0, 0, 0, 0, 0}}}, + {48, 0, 0, 303, 4, {{32, 48, 0, 0, 303, 17}, + {32, 48, 0, 0, 304, 32}, + {32, 52, 0, 0, 303, 13}, + {96, 0, 0, 0, 304, 32}}}, + {48, 0, 202, 0, 3, {{32, 48, 0, 202, 17, 0}, + {32, 48, 0, 208, 32, 0}, + {32, 52, 0, 202, 13, 0}, + {96, 0, 0, 208, 32, 0}}}, + {48, 0, 202, 303, 4, {{32, 48, 0, 202, 303, 17}, + {32, 48, 0, 208, 304, 32}, + {32, 52, 0, 202, 303, 13}, + {96, 0, 0, 208, 304, 32}}}, + {48, 101, 0, 0, 2, {{32, 48, 101, 17, 0, 0}, + {32, 48, 112, 32, 0, 0}, + {32, 52, 101, 13, 0, 0}, + {96, 0, 112, 32, 0, 0}}}, + {48, 101, 0, 303, 4, {{32, 48, 101, 0, 303, 17}, + {32, 48, 112, 0, 304, 32}, + {32, 52, 101, 0, 303, 13}, + {96, 0, 112, 0, 304, 32}}}, + {48, 101, 202, 0, 3, {{32, 48, 101, 202, 17, 0}, + {32, 48, 112, 208, 32, 0}, + {32, 52, 101, 202, 13, 0}, + {96, 0, 112, 208, 32, 0}}}, + {48, 101, 202, 303, 4, {{32, 48, 101, 202, 303, 17}, + {32, 48, 112, 208, 304, 32}, + {32, 52, 101, 202, 303, 13}, + {96, 0, 112, 208, 304, 32}}}, + + // first segment is partially inlined + {49, 0, 0, 0, 1, {{32, 49, 17, 0, 0, 0}, + {32, 64, 32, 0, 0, 0}, + {32, 53, 0, 0, 0, 0}, + {96, 32, 0, 0, 0, 0}}}, + {49, 0, 0, 303, 4, {{32, 49, 0, 0, 303, 17}, + {32, 64, 0, 0, 304, 32}, + {32, 53, 0, 0, 303, 13}, + {96, 32, 0, 0, 304, 32}}}, + {49, 0, 202, 0, 3, {{32, 49, 0, 202, 17, 0}, + {32, 64, 0, 208, 32, 0}, + {32, 53, 0, 202, 13, 0}, + {96, 32, 0, 208, 32, 0}}}, + {49, 0, 202, 303, 4, {{32, 49, 0, 202, 303, 17}, + {32, 64, 0, 208, 304, 32}, + {32, 53, 0, 202, 303, 13}, + {96, 32, 0, 208, 304, 32}}}, + {49, 101, 0, 0, 2, {{32, 49, 101, 17, 0, 0}, + {32, 64, 112, 32, 0, 0}, + {32, 53, 101, 13, 0, 0}, + {96, 32, 112, 32, 0, 0}}}, + {49, 101, 0, 303, 4, {{32, 49, 101, 0, 303, 17}, + {32, 64, 112, 0, 304, 32}, + {32, 53, 101, 0, 303, 13}, + {96, 32, 112, 0, 304, 32}}}, + {49, 101, 202, 0, 3, {{32, 49, 101, 202, 17, 0}, + {32, 64, 112, 208, 32, 0}, + {32, 53, 101, 202, 13, 0}, + {96, 32, 112, 208, 32, 0}}}, + {49, 101, 202, 303, 4, {{32, 49, 101, 202, 303, 17}, + {32, 64, 112, 208, 304, 32}, + {32, 53, 101, 202, 303, 13}, + {96, 32, 112, 208, 304, 32}}}, +}; + +INSTANTIATE_TEST_CASE_P( + RoundTripTests, RoundTripTest, ::testing::Combine( + ::testing::ValuesIn(round_trip_instances), + ::testing::ValuesIn(modes))); + +class RoundTripPerfTest : public RoundTripTestBase {}; + +TEST_P(RoundTripPerfTest, DISABLED_Basic) { + for (int i = 0; i < 100000; i++) { + auto tx_frame = TestFrame::Encode(m_header, m_front, m_middle, m_data); + auto onwire_bl = tx_frame.get_buffer(m_tx_frame_asm); + + Tag rx_tag; + segment_bls_t rx_segment_bls; + ASSERT_TRUE(disassemble_frame(m_rx_frame_asm, onwire_bl, rx_tag, + rx_segment_bls)); + } +} + +static const round_trip_instance_t round_trip_perf_instances[] = { + {41, 250, 0, 0, 2, {{32, 41, 250, 17, 0, 0}, + {32, 48, 256, 32, 0, 0}, + {32, 45, 250, 13, 0, 0}, + {96, 0, 256, 32, 0, 0}}}, + {41, 250, 0, 512, 4, {{32, 41, 250, 0, 512, 17}, + {32, 48, 256, 0, 512, 32}, + {32, 45, 250, 0, 512, 13}, + {96, 0, 256, 0, 512, 32}}}, + {41, 250, 0, 4096, 4, {{32, 41, 250, 0, 4096, 17}, + {32, 48, 256, 0, 4096, 32}, + {32, 45, 250, 0, 4096, 13}, + {96, 0, 256, 0, 4096, 32}}}, + {41, 250, 0, 32768, 4, {{32, 41, 250, 0, 32768, 17}, + {32, 48, 256, 0, 32768, 32}, + {32, 45, 250, 0, 32768, 13}, + {96, 0, 256, 0, 32768, 32}}}, + {41, 250, 0, 131072, 4, {{32, 41, 250, 0, 131072, 17}, + {32, 48, 256, 0, 131072, 32}, + {32, 45, 250, 0, 131072, 13}, + {96, 0, 256, 0, 131072, 32}}}, + {41, 250, 0, 4194304, 4, {{32, 41, 250, 0, 4194304, 17}, + {32, 48, 256, 0, 4194304, 32}, + {32, 45, 250, 0, 4194304, 13}, + {96, 0, 256, 0, 4194304, 32}}}, +}; + +INSTANTIATE_TEST_CASE_P( + RoundTripPerfTests, RoundTripPerfTest, ::testing::Combine( + ::testing::ValuesIn(round_trip_perf_instances), + ::testing::ValuesIn(modes))); + +} // namespace ceph::msgr::v2 + +int main(int argc, char* argv[]) { + vector<const char*> args; + argv_to_vec(argc, (const char**)argv, args); + + auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, + CODE_ENVIRONMENT_UTILITY, + CINIT_FLAG_NO_DEFAULT_CONFIG_FILE); + common_init_finish(g_ceph_context); + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} |