]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #pragma once | |
5 | ||
2a845540 TL |
6 | #include <fmt/format.h> |
7 | ||
20effc67 TL |
8 | #include "crimson/common/log.h" |
9 | #include "crimson/os/seastore/onode_manager/staged-fltree/value.h" | |
10 | ||
11 | namespace crimson::os::seastore::onode { | |
12 | ||
13 | struct test_item_t { | |
14 | using id_t = uint16_t; | |
15 | using magic_t = uint32_t; | |
16 | ||
17 | value_size_t size; | |
18 | id_t id; | |
19 | magic_t magic; | |
20 | ||
21 | value_size_t get_payload_size() const { | |
22 | assert(size > sizeof(value_header_t)); | |
23 | return static_cast<value_size_t>(size - sizeof(value_header_t)); | |
24 | } | |
25 | ||
26 | static test_item_t create(std::size_t _size, std::size_t _id) { | |
27 | ceph_assert(_size <= std::numeric_limits<value_size_t>::max()); | |
28 | ceph_assert(_size > sizeof(value_header_t)); | |
29 | value_size_t size = _size; | |
30 | ||
31 | ceph_assert(_id <= std::numeric_limits<id_t>::max()); | |
32 | id_t id = _id; | |
33 | ||
34 | return {size, id, (magic_t)id * 137}; | |
35 | } | |
36 | }; | |
37 | inline std::ostream& operator<<(std::ostream& os, const test_item_t& item) { | |
38 | return os << "TestItem(#" << item.id << ", " << item.size << "B)"; | |
39 | } | |
40 | ||
2a845540 TL |
41 | enum class delta_op_t : uint8_t { |
42 | UPDATE_ID, | |
43 | UPDATE_TAIL_MAGIC, | |
44 | }; | |
45 | ||
46 | inline std::ostream& operator<<(std::ostream& os, const delta_op_t op) { | |
47 | switch (op) { | |
48 | case delta_op_t::UPDATE_ID: | |
49 | return os << "update_id"; | |
50 | case delta_op_t::UPDATE_TAIL_MAGIC: | |
51 | return os << "update_tail_magic"; | |
52 | default: | |
53 | return os << "unknown"; | |
54 | } | |
55 | } | |
56 | ||
1e59de90 TL |
57 | } // namespace crimson::os::seastore::onode |
58 | ||
59 | #if FMT_VERSION >= 90000 | |
60 | template<> struct fmt::formatter<crimson::os::seastore::onode::delta_op_t> : fmt::ostream_formatter {}; | |
61 | #endif | |
62 | ||
63 | namespace crimson::os::seastore::onode { | |
64 | ||
20effc67 TL |
65 | template <value_magic_t MAGIC, |
66 | string_size_t MAX_NS_SIZE, | |
67 | string_size_t MAX_OID_SIZE, | |
68 | value_size_t MAX_VALUE_PAYLOAD_SIZE, | |
69 | extent_len_t INTERNAL_NODE_SIZE, | |
70 | extent_len_t LEAF_NODE_SIZE, | |
71 | bool DO_SPLIT_CHECK> | |
72 | class TestValue final : public Value { | |
73 | public: | |
74 | static constexpr tree_conf_t TREE_CONF = { | |
75 | MAGIC, | |
76 | MAX_NS_SIZE, | |
77 | MAX_OID_SIZE, | |
78 | MAX_VALUE_PAYLOAD_SIZE, | |
79 | INTERNAL_NODE_SIZE, | |
80 | LEAF_NODE_SIZE, | |
81 | DO_SPLIT_CHECK | |
82 | }; | |
83 | ||
84 | using id_t = test_item_t::id_t; | |
85 | using magic_t = test_item_t::magic_t; | |
86 | struct magic_packed_t { | |
87 | magic_t value; | |
88 | } __attribute__((packed)); | |
89 | ||
90 | private: | |
91 | struct payload_t { | |
92 | id_t id; | |
93 | } __attribute__((packed)); | |
94 | ||
95 | struct Replayable { | |
96 | static void set_id(NodeExtentMutable& payload_mut, id_t id) { | |
97 | auto p_payload = get_write(payload_mut); | |
98 | p_payload->id = id; | |
99 | } | |
100 | ||
101 | static void set_tail_magic(NodeExtentMutable& payload_mut, magic_t magic) { | |
102 | auto length = payload_mut.get_length(); | |
103 | auto offset_magic = length - sizeof(magic_t); | |
104 | payload_mut.copy_in_relative(offset_magic, magic); | |
105 | } | |
106 | ||
107 | private: | |
108 | static payload_t* get_write(NodeExtentMutable& payload_mut) { | |
109 | return reinterpret_cast<payload_t*>(payload_mut.get_write()); | |
110 | } | |
111 | }; | |
112 | ||
113 | public: | |
114 | class Recorder final : public ValueDeltaRecorder { | |
20effc67 TL |
115 | |
116 | public: | |
117 | Recorder(ceph::bufferlist& encoded) | |
118 | : ValueDeltaRecorder(encoded) {} | |
119 | ~Recorder() override = default; | |
120 | ||
121 | void encode_set_id(NodeExtentMutable& payload_mut, id_t id) { | |
122 | auto& encoded = get_encoded(payload_mut); | |
123 | ceph::encode(delta_op_t::UPDATE_ID, encoded); | |
124 | ceph::encode(id, encoded); | |
125 | } | |
126 | ||
127 | void encode_set_tail_magic(NodeExtentMutable& payload_mut, magic_t magic) { | |
128 | auto& encoded = get_encoded(payload_mut); | |
129 | ceph::encode(delta_op_t::UPDATE_TAIL_MAGIC, encoded); | |
130 | ceph::encode(magic, encoded); | |
131 | } | |
132 | ||
133 | protected: | |
134 | value_magic_t get_header_magic() const override { | |
135 | return TREE_CONF.value_magic; | |
136 | } | |
137 | ||
138 | void apply_value_delta(ceph::bufferlist::const_iterator& delta, | |
139 | NodeExtentMutable& payload_mut, | |
140 | laddr_t value_addr) override { | |
141 | delta_op_t op; | |
142 | try { | |
143 | ceph::decode(op, delta); | |
144 | switch (op) { | |
145 | case delta_op_t::UPDATE_ID: { | |
146 | logger().debug("OTree::TestValue::Replay: decoding UPDATE_ID ..."); | |
147 | id_t id; | |
148 | ceph::decode(id, delta); | |
149 | logger().debug("OTree::TestValue::Replay: apply id={} ...", id); | |
150 | Replayable::set_id(payload_mut, id); | |
151 | break; | |
152 | } | |
153 | case delta_op_t::UPDATE_TAIL_MAGIC: { | |
154 | logger().debug("OTree::TestValue::Replay: decoding UPDATE_TAIL_MAGIC ..."); | |
155 | magic_t magic; | |
156 | ceph::decode(magic, delta); | |
157 | logger().debug("OTree::TestValue::Replay: apply magic={} ...", magic); | |
158 | Replayable::set_tail_magic(payload_mut, magic); | |
159 | break; | |
160 | } | |
161 | default: | |
162 | logger().error("OTree::TestValue::Replay: got unknown op {} when replay {:#x}+{:#x}", | |
163 | op, value_addr, payload_mut.get_length()); | |
164 | ceph_abort(); | |
165 | } | |
166 | } catch (buffer::error& e) { | |
167 | logger().error("OTree::TestValue::Replay: got decode error {} when replay {:#x}+{:#x}", | |
1e59de90 | 168 | e.what(), value_addr, payload_mut.get_length()); |
20effc67 TL |
169 | ceph_abort(); |
170 | } | |
171 | } | |
172 | ||
173 | private: | |
174 | seastar::logger& logger() { | |
175 | return crimson::get_logger(ceph_subsys_test); | |
176 | } | |
177 | }; | |
178 | ||
179 | TestValue(NodeExtentManager& nm, const ValueBuilder& vb, Ref<tree_cursor_t>& p_cursor) | |
180 | : Value(nm, vb, p_cursor) {} | |
181 | ~TestValue() override = default; | |
182 | ||
183 | id_t get_id() const { | |
184 | return read_payload<payload_t>()->id; | |
185 | } | |
186 | void set_id_replayable(Transaction& t, id_t id) { | |
187 | auto value_mutable = prepare_mutate_payload<payload_t, Recorder>(t); | |
188 | if (value_mutable.second) { | |
189 | value_mutable.second->encode_set_id(value_mutable.first, id); | |
190 | } | |
191 | Replayable::set_id(value_mutable.first, id); | |
192 | } | |
193 | ||
194 | magic_t get_tail_magic() const { | |
195 | auto p_payload = read_payload<payload_t>(); | |
196 | auto offset_magic = get_payload_size() - sizeof(magic_t); | |
197 | auto p_magic = reinterpret_cast<const char*>(p_payload) + offset_magic; | |
198 | return reinterpret_cast<const magic_packed_t*>(p_magic)->value; | |
199 | } | |
200 | void set_tail_magic_replayable(Transaction& t, magic_t magic) { | |
201 | auto value_mutable = prepare_mutate_payload<payload_t, Recorder>(t); | |
202 | if (value_mutable.second) { | |
203 | value_mutable.second->encode_set_tail_magic(value_mutable.first, magic); | |
204 | } | |
205 | Replayable::set_tail_magic(value_mutable.first, magic); | |
206 | } | |
207 | ||
208 | /* | |
209 | * tree_util.h related interfaces | |
210 | */ | |
211 | ||
212 | using item_t = test_item_t; | |
213 | ||
214 | void initialize(Transaction& t, const item_t& item) { | |
215 | ceph_assert(get_payload_size() + sizeof(value_header_t) == item.size); | |
216 | set_id_replayable(t, item.id); | |
217 | set_tail_magic_replayable(t, item.magic); | |
218 | } | |
219 | ||
220 | void validate(const item_t& item) const { | |
221 | ceph_assert(get_payload_size() + sizeof(value_header_t) == item.size); | |
222 | ceph_assert(get_id() == item.id); | |
223 | ceph_assert(get_tail_magic() == item.magic); | |
224 | } | |
225 | }; | |
226 | ||
227 | using UnboundedValue = TestValue< | |
228 | value_magic_t::TEST_UNBOUND, 4096, 4096, 4096, 4096, 4096, false>; | |
229 | using BoundedValue = TestValue< | |
230 | value_magic_t::TEST_BOUNDED, 320, 320, 640, 4096, 4096, true>; | |
231 | // should be the same configuration with FLTreeOnode | |
232 | using ExtendedValue = TestValue< | |
233 | value_magic_t::TEST_EXTENDED, 256, 2048, 1200, 8192, 16384, true>; | |
234 | ||
235 | } | |
1e59de90 TL |
236 | |
237 | #if FMT_VERSION >= 90000 | |
238 | template<> | |
239 | struct fmt::formatter<crimson::os::seastore::onode::test_item_t> : fmt::ostream_formatter {}; | |
240 | #endif |