1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2020 Red Hat
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
15 #include "msg/async/frames_v2.h"
22 #include "auth/Auth.h"
23 #include "common/ceph_argparse.h"
24 #include "global/global_init.h"
25 #include "global/global_context.h"
26 #include "include/Context.h"
28 #include <gtest/gtest.h>
30 namespace ceph::msgr::v2
{
32 // MessageFrame with the first segment not fixed to ceph_msg_header2
33 struct TestFrame
: Frame
<TestFrame
,
35 segment_t::DEFAULT_ALIGNMENT
,
36 segment_t::DEFAULT_ALIGNMENT
,
37 segment_t::DEFAULT_ALIGNMENT
,
38 segment_t::PAGE_SIZE_ALIGNMENT
> {
39 static constexpr Tag tag
= static_cast<Tag
>(123);
41 static TestFrame
Encode(const bufferlist
& header
,
42 const bufferlist
& front
,
43 const bufferlist
& middle
,
44 const bufferlist
& data
) {
46 f
.segments
[SegmentIndex::Msg::HEADER
] = header
;
47 f
.segments
[SegmentIndex::Msg::FRONT
] = front
;
48 f
.segments
[SegmentIndex::Msg::MIDDLE
] = middle
;
49 f
.segments
[SegmentIndex::Msg::DATA
] = data
;
51 // discard cached crcs for perf tests
52 f
.segments
[SegmentIndex::Msg::HEADER
].invalidate_crc();
53 f
.segments
[SegmentIndex::Msg::FRONT
].invalidate_crc();
54 f
.segments
[SegmentIndex::Msg::MIDDLE
].invalidate_crc();
55 f
.segments
[SegmentIndex::Msg::DATA
].invalidate_crc();
59 static TestFrame
Decode(segment_bls_t
& segment_bls
) {
61 // Transfer segments' bufferlists. If segment_bls contains
62 // less than SegmentsNumV segments, the missing ones will be
64 for (size_t i
= 0; i
< segment_bls
.size(); i
++) {
65 f
.segments
[i
] = std::move(segment_bls
[i
]);
70 bufferlist
& header() {
71 return segments
[SegmentIndex::Msg::HEADER
];
74 return segments
[SegmentIndex::Msg::FRONT
];
76 bufferlist
& middle() {
77 return segments
[SegmentIndex::Msg::MIDDLE
];
80 return segments
[SegmentIndex::Msg::DATA
];
92 static std::ostream
& operator<<(std::ostream
& os
, const mode_t
& m
) {
93 os
<< "msgr2." << (m
.is_rev1
? "1" : "0")
94 << (m
.is_secure
? "-secure" : "-crc");
98 static const mode_t modes
[] = {
105 struct round_trip_instance_t
{
111 // expected number of segments (same for each mode)
113 // expected layout (different for each mode)
114 uint32_t onwire_lens
[4][MAX_NUM_SEGMENTS
+ 2];
117 static std::ostream
& operator<<(std::ostream
& os
,
118 const round_trip_instance_t
& rti
) {
119 os
<< rti
.header_len
<< "+" << rti
.front_len
<< "+"
120 << rti
.middle_len
<< "+" << rti
.data_len
;
124 static bufferlist
make_bufferlist(size_t len
, char c
) {
128 bl
.append(std::string(len
, c
));
133 bool disassemble_frame(FrameAssembler
& frame_asm
, bufferlist
& frame_bl
,
134 Tag
& tag
, segment_bls_t
& segment_bls
) {
135 bufferlist preamble_bl
;
136 frame_bl
.splice(0, frame_asm
.get_preamble_onwire_len(), &preamble_bl
);
137 tag
= frame_asm
.disassemble_preamble(preamble_bl
);
140 size_t seg_idx
= segment_bls
.size();
141 segment_bls
.emplace_back();
143 uint32_t onwire_len
= frame_asm
.get_segment_onwire_len(seg_idx
);
144 if (onwire_len
> 0) {
145 frame_bl
.splice(0, onwire_len
, &segment_bls
.back());
147 } while (segment_bls
.size() < frame_asm
.get_num_segments());
149 bufferlist epilogue_bl
;
150 uint32_t epilogue_onwire_len
= frame_asm
.get_epilogue_onwire_len();
151 if (epilogue_onwire_len
> 0) {
152 frame_bl
.splice(0, epilogue_onwire_len
, &epilogue_bl
);
154 frame_asm
.disassemble_first_segment(preamble_bl
, segment_bls
[0]);
155 return frame_asm
.disassemble_remaining_segments(segment_bls
.data(),
159 class RoundTripTestBase
: public ::testing::TestWithParam
<
160 std::tuple
<round_trip_instance_t
, mode_t
>> {
163 : m_tx_frame_asm(&m_tx_crypto
, std::get
<1>(GetParam()).is_rev1
),
164 m_rx_frame_asm(&m_rx_crypto
, std::get
<1>(GetParam()).is_rev1
),
165 m_header(make_bufferlist(std::get
<0>(GetParam()).header_len
, 'H')),
166 m_front(make_bufferlist(std::get
<0>(GetParam()).front_len
, 'F')),
167 m_middle(make_bufferlist(std::get
<0>(GetParam()).middle_len
, 'M')),
168 m_data(make_bufferlist(std::get
<0>(GetParam()).data_len
, 'D')) {
169 const auto& m
= std::get
<1>(GetParam());
171 AuthConnectionMeta auth_meta
;
172 auth_meta
.con_mode
= CEPH_CON_MODE_SECURE
;
173 // see AuthConnectionMeta::get_connection_secret_length()
174 auth_meta
.connection_secret
.resize(64);
175 g_ceph_context
->random()->get_bytes(auth_meta
.connection_secret
.data(),
176 auth_meta
.connection_secret
.size());
177 m_tx_crypto
= ceph::crypto::onwire::rxtx_t::create_handler_pair(
178 g_ceph_context
, auth_meta
, /*new_nonce_format=*/m
.is_rev1
,
180 m_rx_crypto
= ceph::crypto::onwire::rxtx_t::create_handler_pair(
181 g_ceph_context
, auth_meta
, /*new_nonce_format=*/m
.is_rev1
,
186 void check_frame_assembler(const FrameAssembler
& frame_asm
) {
187 const auto& [rti
, m
] = GetParam();
188 const auto& onwire_lens
= rti
.onwire_lens
[m
.is_rev1
<< 1 | m
.is_secure
];
189 EXPECT_EQ(rti
.header_len
+ rti
.front_len
+ rti
.middle_len
+ rti
.data_len
,
190 frame_asm
.get_frame_logical_len());
191 ASSERT_EQ(rti
.num_segments
, frame_asm
.get_num_segments());
192 EXPECT_EQ(onwire_lens
[0], frame_asm
.get_preamble_onwire_len());
193 for (size_t i
= 0; i
< rti
.num_segments
; i
++) {
194 EXPECT_EQ(onwire_lens
[i
+ 1], frame_asm
.get_segment_onwire_len(i
));
196 EXPECT_EQ(onwire_lens
[rti
.num_segments
+ 1],
197 frame_asm
.get_epilogue_onwire_len());
198 EXPECT_EQ(std::accumulate(std::begin(onwire_lens
), std::end(onwire_lens
),
200 frame_asm
.get_frame_onwire_len());
203 void test_round_trip() {
204 auto tx_frame
= TestFrame::Encode(m_header
, m_front
, m_middle
, m_data
);
205 auto onwire_bl
= tx_frame
.get_buffer(m_tx_frame_asm
);
206 check_frame_assembler(m_tx_frame_asm
);
207 EXPECT_EQ(m_tx_frame_asm
.get_frame_onwire_len(), onwire_bl
.length());
210 segment_bls_t rx_segment_bls
;
211 EXPECT_TRUE(disassemble_frame(m_rx_frame_asm
, onwire_bl
, rx_tag
,
213 check_frame_assembler(m_rx_frame_asm
);
214 EXPECT_EQ(0, onwire_bl
.length());
215 EXPECT_EQ(TestFrame::tag
, rx_tag
);
216 EXPECT_EQ(m_rx_frame_asm
.get_num_segments(), rx_segment_bls
.size());
218 auto rx_frame
= TestFrame::Decode(rx_segment_bls
);
219 EXPECT_TRUE(m_header
.contents_equal(rx_frame
.header()));
220 EXPECT_TRUE(m_front
.contents_equal(rx_frame
.front()));
221 EXPECT_TRUE(m_middle
.contents_equal(rx_frame
.middle()));
222 EXPECT_TRUE(m_data
.contents_equal(rx_frame
.data()));
225 ceph::crypto::onwire::rxtx_t m_tx_crypto
;
226 ceph::crypto::onwire::rxtx_t m_rx_crypto
;
227 FrameAssembler m_tx_frame_asm
;
228 FrameAssembler m_rx_frame_asm
;
230 const bufferlist m_header
;
231 const bufferlist m_front
;
232 const bufferlist m_middle
;
233 const bufferlist m_data
;
236 class RoundTripTest
: public RoundTripTestBase
{};
238 TEST_P(RoundTripTest
, Basic
) {
242 TEST_P(RoundTripTest
, Reuse
) {
243 for (int i
= 0; i
< 3; i
++) {
248 static const round_trip_instance_t round_trip_instances
[] = {
249 // first segment is empty
250 { 0, 0, 0, 0, 1, {{32, 0, 17, 0, 0, 0},
251 {32, 0, 32, 0, 0, 0},
253 {96, 0, 0, 0, 0, 0}}},
254 { 0, 0, 0, 303, 4, {{32, 0, 0, 0, 303, 17},
255 {32, 0, 0, 0, 304, 32},
256 {32, 0, 0, 0, 303, 13},
257 {96, 0, 0, 0, 304, 32}}},
258 { 0, 0, 202, 0, 3, {{32, 0, 0, 202, 17, 0},
259 {32, 0, 0, 208, 32, 0},
260 {32, 0, 0, 202, 13, 0},
261 {96, 0, 0, 208, 32, 0}}},
262 { 0, 0, 202, 303, 4, {{32, 0, 0, 202, 303, 17},
263 {32, 0, 0, 208, 304, 32},
264 {32, 0, 0, 202, 303, 13},
265 {96, 0, 0, 208, 304, 32}}},
266 { 0, 101, 0, 0, 2, {{32, 0, 101, 17, 0, 0},
267 {32, 0, 112, 32, 0, 0},
268 {32, 0, 101, 13, 0, 0},
269 {96, 0, 112, 32, 0, 0}}},
270 { 0, 101, 0, 303, 4, {{32, 0, 101, 0, 303, 17},
271 {32, 0, 112, 0, 304, 32},
272 {32, 0, 101, 0, 303, 13},
273 {96, 0, 112, 0, 304, 32}}},
274 { 0, 101, 202, 0, 3, {{32, 0, 101, 202, 17, 0},
275 {32, 0, 112, 208, 32, 0},
276 {32, 0, 101, 202, 13, 0},
277 {96, 0, 112, 208, 32, 0}}},
278 { 0, 101, 202, 303, 4, {{32, 0, 101, 202, 303, 17},
279 {32, 0, 112, 208, 304, 32},
280 {32, 0, 101, 202, 303, 13},
281 {96, 0, 112, 208, 304, 32}}},
283 // first segment is fully inlined, inline buffer is not full
284 { 1, 0, 0, 0, 1, {{32, 1, 17, 0, 0, 0},
285 {32, 16, 32, 0, 0, 0},
287 {96, 0, 0, 0, 0, 0}}},
288 { 1, 0, 0, 303, 4, {{32, 1, 0, 0, 303, 17},
289 {32, 16, 0, 0, 304, 32},
290 {32, 5, 0, 0, 303, 13},
291 {96, 0, 0, 0, 304, 32}}},
292 { 1, 0, 202, 0, 3, {{32, 1, 0, 202, 17, 0},
293 {32, 16, 0, 208, 32, 0},
294 {32, 5, 0, 202, 13, 0},
295 {96, 0, 0, 208, 32, 0}}},
296 { 1, 0, 202, 303, 4, {{32, 1, 0, 202, 303, 17},
297 {32, 16, 0, 208, 304, 32},
298 {32, 5, 0, 202, 303, 13},
299 {96, 0, 0, 208, 304, 32}}},
300 { 1, 101, 0, 0, 2, {{32, 1, 101, 17, 0, 0},
301 {32, 16, 112, 32, 0, 0},
302 {32, 5, 101, 13, 0, 0},
303 {96, 0, 112, 32, 0, 0}}},
304 { 1, 101, 0, 303, 4, {{32, 1, 101, 0, 303, 17},
305 {32, 16, 112, 0, 304, 32},
306 {32, 5, 101, 0, 303, 13},
307 {96, 0, 112, 0, 304, 32}}},
308 { 1, 101, 202, 0, 3, {{32, 1, 101, 202, 17, 0},
309 {32, 16, 112, 208, 32, 0},
310 {32, 5, 101, 202, 13, 0},
311 {96, 0, 112, 208, 32, 0}}},
312 { 1, 101, 202, 303, 4, {{32, 1, 101, 202, 303, 17},
313 {32, 16, 112, 208, 304, 32},
314 {32, 5, 101, 202, 303, 13},
315 {96, 0, 112, 208, 304, 32}}},
317 // first segment is fully inlined, inline buffer is full
318 {48, 0, 0, 0, 1, {{32, 48, 17, 0, 0, 0},
319 {32, 48, 32, 0, 0, 0},
320 {32, 52, 0, 0, 0, 0},
321 {96, 0, 0, 0, 0, 0}}},
322 {48, 0, 0, 303, 4, {{32, 48, 0, 0, 303, 17},
323 {32, 48, 0, 0, 304, 32},
324 {32, 52, 0, 0, 303, 13},
325 {96, 0, 0, 0, 304, 32}}},
326 {48, 0, 202, 0, 3, {{32, 48, 0, 202, 17, 0},
327 {32, 48, 0, 208, 32, 0},
328 {32, 52, 0, 202, 13, 0},
329 {96, 0, 0, 208, 32, 0}}},
330 {48, 0, 202, 303, 4, {{32, 48, 0, 202, 303, 17},
331 {32, 48, 0, 208, 304, 32},
332 {32, 52, 0, 202, 303, 13},
333 {96, 0, 0, 208, 304, 32}}},
334 {48, 101, 0, 0, 2, {{32, 48, 101, 17, 0, 0},
335 {32, 48, 112, 32, 0, 0},
336 {32, 52, 101, 13, 0, 0},
337 {96, 0, 112, 32, 0, 0}}},
338 {48, 101, 0, 303, 4, {{32, 48, 101, 0, 303, 17},
339 {32, 48, 112, 0, 304, 32},
340 {32, 52, 101, 0, 303, 13},
341 {96, 0, 112, 0, 304, 32}}},
342 {48, 101, 202, 0, 3, {{32, 48, 101, 202, 17, 0},
343 {32, 48, 112, 208, 32, 0},
344 {32, 52, 101, 202, 13, 0},
345 {96, 0, 112, 208, 32, 0}}},
346 {48, 101, 202, 303, 4, {{32, 48, 101, 202, 303, 17},
347 {32, 48, 112, 208, 304, 32},
348 {32, 52, 101, 202, 303, 13},
349 {96, 0, 112, 208, 304, 32}}},
351 // first segment is partially inlined
352 {49, 0, 0, 0, 1, {{32, 49, 17, 0, 0, 0},
353 {32, 64, 32, 0, 0, 0},
354 {32, 53, 0, 0, 0, 0},
355 {96, 32, 0, 0, 0, 0}}},
356 {49, 0, 0, 303, 4, {{32, 49, 0, 0, 303, 17},
357 {32, 64, 0, 0, 304, 32},
358 {32, 53, 0, 0, 303, 13},
359 {96, 32, 0, 0, 304, 32}}},
360 {49, 0, 202, 0, 3, {{32, 49, 0, 202, 17, 0},
361 {32, 64, 0, 208, 32, 0},
362 {32, 53, 0, 202, 13, 0},
363 {96, 32, 0, 208, 32, 0}}},
364 {49, 0, 202, 303, 4, {{32, 49, 0, 202, 303, 17},
365 {32, 64, 0, 208, 304, 32},
366 {32, 53, 0, 202, 303, 13},
367 {96, 32, 0, 208, 304, 32}}},
368 {49, 101, 0, 0, 2, {{32, 49, 101, 17, 0, 0},
369 {32, 64, 112, 32, 0, 0},
370 {32, 53, 101, 13, 0, 0},
371 {96, 32, 112, 32, 0, 0}}},
372 {49, 101, 0, 303, 4, {{32, 49, 101, 0, 303, 17},
373 {32, 64, 112, 0, 304, 32},
374 {32, 53, 101, 0, 303, 13},
375 {96, 32, 112, 0, 304, 32}}},
376 {49, 101, 202, 0, 3, {{32, 49, 101, 202, 17, 0},
377 {32, 64, 112, 208, 32, 0},
378 {32, 53, 101, 202, 13, 0},
379 {96, 32, 112, 208, 32, 0}}},
380 {49, 101, 202, 303, 4, {{32, 49, 101, 202, 303, 17},
381 {32, 64, 112, 208, 304, 32},
382 {32, 53, 101, 202, 303, 13},
383 {96, 32, 112, 208, 304, 32}}},
386 INSTANTIATE_TEST_SUITE_P(
387 RoundTripTests
, RoundTripTest
, ::testing::Combine(
388 ::testing::ValuesIn(round_trip_instances
),
389 ::testing::ValuesIn(modes
)));
391 class RoundTripPerfTest
: public RoundTripTestBase
{};
393 TEST_P(RoundTripPerfTest
, DISABLED_Basic
) {
394 for (int i
= 0; i
< 100000; i
++) {
395 auto tx_frame
= TestFrame::Encode(m_header
, m_front
, m_middle
, m_data
);
396 auto onwire_bl
= tx_frame
.get_buffer(m_tx_frame_asm
);
399 segment_bls_t rx_segment_bls
;
400 ASSERT_TRUE(disassemble_frame(m_rx_frame_asm
, onwire_bl
, rx_tag
,
405 static const round_trip_instance_t round_trip_perf_instances
[] = {
406 {41, 250, 0, 0, 2, {{32, 41, 250, 17, 0, 0},
407 {32, 48, 256, 32, 0, 0},
408 {32, 45, 250, 13, 0, 0},
409 {96, 0, 256, 32, 0, 0}}},
410 {41, 250, 0, 512, 4, {{32, 41, 250, 0, 512, 17},
411 {32, 48, 256, 0, 512, 32},
412 {32, 45, 250, 0, 512, 13},
413 {96, 0, 256, 0, 512, 32}}},
414 {41, 250, 0, 4096, 4, {{32, 41, 250, 0, 4096, 17},
415 {32, 48, 256, 0, 4096, 32},
416 {32, 45, 250, 0, 4096, 13},
417 {96, 0, 256, 0, 4096, 32}}},
418 {41, 250, 0, 32768, 4, {{32, 41, 250, 0, 32768, 17},
419 {32, 48, 256, 0, 32768, 32},
420 {32, 45, 250, 0, 32768, 13},
421 {96, 0, 256, 0, 32768, 32}}},
422 {41, 250, 0, 131072, 4, {{32, 41, 250, 0, 131072, 17},
423 {32, 48, 256, 0, 131072, 32},
424 {32, 45, 250, 0, 131072, 13},
425 {96, 0, 256, 0, 131072, 32}}},
426 {41, 250, 0, 4194304, 4, {{32, 41, 250, 0, 4194304, 17},
427 {32, 48, 256, 0, 4194304, 32},
428 {32, 45, 250, 0, 4194304, 13},
429 {96, 0, 256, 0, 4194304, 32}}},
432 INSTANTIATE_TEST_SUITE_P(
433 RoundTripPerfTests
, RoundTripPerfTest
, ::testing::Combine(
434 ::testing::ValuesIn(round_trip_perf_instances
),
435 ::testing::ValuesIn(modes
)));
437 } // namespace ceph::msgr::v2
439 int main(int argc
, char* argv
[]) {
440 vector
<const char*> args
;
441 argv_to_vec(argc
, (const char**)argv
, args
);
443 auto cct
= global_init(NULL
, args
, CEPH_ENTITY_TYPE_CLIENT
,
444 CODE_ENVIRONMENT_UTILITY
,
445 CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
);
446 common_init_finish(g_ceph_context
);
448 ::testing::InitGoogleTest(&argc
, argv
);
449 return RUN_ALL_TESTS();