bmv2
Designing your own switch target with bmv2
Loading...
Searching...
No Matches
match_units.h
1/* Copyright 2013-present Barefoot Networks, Inc.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16/*
17 * Antonin Bas
18 *
19 */
20
21#ifndef BM_BM_SIM_MATCH_UNITS_H_
22#define BM_BM_SIM_MATCH_UNITS_H_
23
24#include <atomic>
25#include <cstddef> // for ptrdiff_t
26#include <iosfwd>
27#include <iterator>
28#include <memory>
29#include <string>
30#include <unordered_map>
31#include <utility> // for pair<>
32#include <vector>
33
34#include "match_key_types.h"
35#include "match_error_codes.h"
36#include "lookup_structures.h"
37#include "bytecontainer.h"
38#include "packet.h"
39#include "handle_mgr.h"
40#include "counters.h"
41#include "meters.h"
42#include "phv_forward.h"
43
44namespace bm {
45
46class P4Objects; // forward declaration for deserialize
47
48// using string and not ByteContainer for efficiency
49struct MatchKeyParam {
50 // order is important, implementation sorts match fields according to their
51 // match type based on this order
52 // VALID used to be before RANGE, but when RANGE support was added, it was
53 // easier (implementation-wise) to put RANGE first. Note that this order only
54 // matters for the implementation.
55 enum class Type {
56 RANGE,
57 VALID,
58 EXACT,
59 LPM,
60 TERNARY
61 };
62
63 MatchKeyParam(const Type &type, std::string key)
64 : type(type), key(std::move(key)) { }
65
66 MatchKeyParam(const Type &type, std::string key, std::string mask)
67 : type(type), key(std::move(key)), mask(std::move(mask)) { }
68
69 MatchKeyParam(const Type &type, std::string key, int prefix_length)
70 : type(type), key(std::move(key)), prefix_length(prefix_length) { }
71
72 friend std::ostream& operator<<(std::ostream &out, const MatchKeyParam &p);
73
74 static std::string type_to_string(Type t);
75
76 Type type;
77 std::string key; // start for range
78 std::string mask{}; // optional, end for range
79 int prefix_length{0}; // optional
80};
81
82
83namespace detail {
84
85class MatchKeyBuilderHelper;
86
87} // namespace detail
88
89// Fields should be pushed in the P4 program (i.e. JSON) order. Internally, they
90// will be re-ordered for a more efficient implementation.
91class MatchKeyBuilder {
92 friend class detail::MatchKeyBuilderHelper;
93 public:
94 void push_back_field(header_id_t header, int field_offset, size_t nbits,
95 MatchKeyParam::Type mtype, const std::string &name = "");
96
97 void push_back_field(header_id_t header, int field_offset, size_t nbits,
98 const ByteContainer &mask, MatchKeyParam::Type mtype,
99 const std::string &name = "");
100
101 void push_back_valid_header(header_id_t header, const std::string &name = "");
102
103 void apply_big_mask(ByteContainer *key) const;
104
105 void operator()(const PHV &phv, ByteContainer *key) const;
106
107 std::vector<std::string> key_to_fields(const ByteContainer &key) const;
108
109 std::string key_to_string(const ByteContainer &key,
110 std::string separator = "",
111 bool upper_case = false) const;
112
113 void build();
114
115 template <typename E>
116 std::vector<MatchKeyParam> entry_to_match_params(const E &entry) const;
117
118 template <typename E>
119 E match_params_to_entry(const std::vector<MatchKeyParam> &params) const;
120
121 bool match_params_sanity_check(
122 const std::vector<MatchKeyParam> &params) const;
123
124 size_t get_nbytes_key() const { return nbytes_key; }
125
126 // get_size_key returns the number of "fields" in the key.
127 size_t get_size_key() const { return key_input.size(); }
128
129 const std::string &get_name(size_t idx) const { return name_map.get(idx); }
130
131 size_t max_name_size() const { return name_map.max_size(); }
132
133 private:
134 struct KeyF {
135 header_id_t header;
136 int f_offset;
137 MatchKeyParam::Type mtype;
138 size_t nbits;
139 };
140
141 struct NameMap {
142 void push_back(const std::string &name);
143 const std::string &get(size_t idx) const;
144 size_t max_size() const;
145
146 std::vector<std::string> names{};
147 size_t max_s{0};
148 };
149
150 // takes ownership of input
151 void push_back(KeyF &&input, const ByteContainer &mask,
152 const std::string &name);
153
154 std::vector<KeyF> key_input{};
155 size_t nbytes_key{0};
156 bool has_big_mask{false};
157 ByteContainer big_mask{};
158 // maps the position of the field in the original P4 key to its actual
159 // position in the implementation-specific key. In the implementation, RANGE
160 // match keys come first, followed by VALID, EXACT, then LPM and TERNARY.
161 std::vector<size_t> key_mapping{};
162 // inverse of key_mapping, could be handy
163 std::vector<size_t> inv_mapping{};
164 std::vector<size_t> key_offsets{};
165 NameMap name_map{};
166 bool built{false};
167 std::vector<ByteContainer> masks{};
168};
169
170namespace MatchUnit {
171
172struct AtomicTimestamp {
173 std::atomic<uint64_t> ms_{};
174
175 AtomicTimestamp() { }
176
177 template <typename T>
178 explicit AtomicTimestamp(const T &tp) {
179 ms_ = std::chrono::duration_cast<std::chrono::milliseconds>(
180 tp.time_since_epoch()).count();
181 }
182
183 explicit AtomicTimestamp(uint64_t ms)
184 : ms_(ms) { }
185
186 template <typename T>
187 void set(const T &tp) {
188 ms_ = std::chrono::duration_cast<std::chrono::milliseconds>(
189 tp.time_since_epoch()).count();
190 }
191
192 void set(uint64_t ms) {
193 ms_ = ms;
194 }
195
196 uint64_t get_ms() const {
197 return ms_;
198 }
199
200 /* don't need these (for now?), so remove them */
201 AtomicTimestamp(const AtomicTimestamp &other) = delete;
202 AtomicTimestamp &operator=(const AtomicTimestamp &other) = delete;
203
204 /* std::atomic<T> is non-movable so I have to define this myself */
205 AtomicTimestamp(AtomicTimestamp &&other)
206 : ms_(other.ms_.load()) { }
207 AtomicTimestamp &operator=(AtomicTimestamp &&other) {
208 ms_ = other.ms_.load();
209 return *this;
210 }
211};
212
213struct EntryMeta {
214 using clock = Packet::clock;
215
216 AtomicTimestamp ts{};
217 uint32_t timeout_ms{0};
218 Counter counter{};
219 uint32_t version{};
220
221 void reset() {
222 counter.reset_counter();
223 ts.set(clock::now());
224 }
225};
226
227} // namespace MatchUnit
228
229class MatchUnitAbstract_ {
230 public:
231 friend class handle_iterator;
232 // Iterator for entry handles
233 // having a const / non-const flavor would not make sense here: handles cannot
234 // be modified
235 class handle_iterator {
236 public:
237 using iterator_category = std::forward_iterator_tag;
238 using value_type = handle_t;
239 using difference_type = std::ptrdiff_t; // default for std::iterator
240 using pointer = handle_t*;
241 using reference = handle_t&;
242
243 handle_iterator(const MatchUnitAbstract_ *mu, HandleMgr::const_iterator it);
244
245 const entry_handle_t &operator*() const {
246 assert(it != mu->handles.end() && "Invalid iterator dereference.");
247 return handle;
248 }
249
250 const entry_handle_t *operator->() const {
251 assert(it != mu->handles.end() && "Invalid iterator dereference.");
252 return &handle;
253 }
254
255 bool operator==(const handle_iterator &other) const {
256 return (mu == other.mu) && (it == other.it);
257 }
258
259 bool operator!=(const handle_iterator &other) const {
260 return !(*this == other);
261 }
262
263 handle_iterator &operator++();
264
265 const handle_iterator operator++(int) {
266 // Use operator++()
267 const handle_iterator old(*this);
268 ++(*this);
269 return old;
270 }
271
272 private:
273 const MatchUnitAbstract_ *mu{nullptr};
274 HandleMgr::const_iterator it;
275 entry_handle_t handle{};
276 };
277
278 public:
279 MatchUnitAbstract_(size_t size, const MatchKeyBuilder &key_builder);
280
281 size_t get_num_entries() const { return num_entries; }
282
283 size_t get_size() const { return size; }
284
285 size_t get_nbytes_key() const { return nbytes_key; }
286
287 size_t get_size_key() const { return size_key; }
288
289 bool valid_handle(entry_handle_t handle) const;
290
291 MatchUnit::EntryMeta &get_entry_meta(entry_handle_t handle);
292 const MatchUnit::EntryMeta &get_entry_meta(entry_handle_t handle) const;
293
294 void reset_counters();
295
296 void set_direct_meters(MeterArray *meter_array);
297
298 Meter &get_meter(entry_handle_t handle);
299
300 MatchErrorCode set_entry_ttl(entry_handle_t handle, unsigned int ttl_ms);
301
302 void sweep_entries(std::vector<entry_handle_t> *entries) const;
303
304 void dump_key_params(std::ostream *out,
305 const std::vector<MatchKeyParam> &params,
306 int priority = -1) const;
307
308 // TODO(antonin): add an iterator for entries to MatchUnitGeneric<K, V> ?
309 handle_iterator handles_begin() const;
310 handle_iterator handles_end() const;
311
312 protected:
313 MatchErrorCode get_and_set_handle(internal_handle_t *handle);
314 MatchErrorCode unset_handle(internal_handle_t handle);
315 bool valid_handle_(internal_handle_t handle) const;
316
317 void build_key(const PHV &phv, ByteContainer *key) const {
318 match_key_builder(phv, key);
319 }
320
321 std::string key_to_string(const ByteContainer &key) const {
322 return match_key_builder.key_to_string(key);
323 }
324
325 std::string key_to_string_with_names(const ByteContainer &key) const;
326
327 void update_counters(Counter *c, const Packet &pkt) {
328 c->increment_counter(pkt);
329 }
330
331 void update_ts(MatchUnit::AtomicTimestamp *ts, const Packet &pkt) {
332 ts->set(pkt.get_ingress_ts_ms());
333 }
334
335 protected:
336 ~MatchUnitAbstract_() { }
337
338 protected:
339 size_t size{0};
340 size_t num_entries{0};
341 size_t nbytes_key;
342 size_t size_key;
343 HandleMgr handles{};
344 MatchKeyBuilder match_key_builder;
345 std::vector<MatchUnit::EntryMeta> entry_meta{};
346 // non-owning pointer, the meter array still belongs to P4Objects
347 MeterArray *direct_meters{nullptr};
348};
349
350template <typename V>
351class MatchUnitAbstract : public MatchUnitAbstract_ {
352 public:
353 struct MatchUnitLookup {
354 MatchUnitLookup(entry_handle_t handle, const V *value)
355 : handle(handle), value(value) { }
356
357 bool found() const { return (value != nullptr); }
358
359 static MatchUnitLookup empty_entry() { return MatchUnitLookup(0, nullptr); }
360
361 entry_handle_t handle{0};
362 const V *value{nullptr};
363 };
364
365 public:
366 MatchUnitAbstract(size_t size, const MatchKeyBuilder &match_key_builder)
367 : MatchUnitAbstract_(size, match_key_builder) { }
368
369 virtual ~MatchUnitAbstract() { }
370
371 MatchUnitLookup lookup(const Packet &pkt);
372
373 MatchErrorCode add_entry(const std::vector<MatchKeyParam> &match_key,
374 V value, // by value for possible std::move
375 entry_handle_t *handle,
376 int priority = -1);
377
378 MatchErrorCode delete_entry(entry_handle_t handle);
379
380 MatchErrorCode modify_entry(entry_handle_t handle, V value);
381
382 MatchErrorCode get_value(entry_handle_t handle, const V **value);
383
384 MatchErrorCode get_entry(entry_handle_t handle,
385 std::vector<MatchKeyParam> *match_key,
386 const V **value, int *priority = nullptr) const;
387
388 MatchErrorCode retrieve_handle(const std::vector<MatchKeyParam> &match_key,
389 entry_handle_t *handle,
390 int priority = -1) const;
391
392 // TODO(antonin): move this one level up in class hierarchy?
393 // will return an empty string if the handle is not valid
394 // otherwise will return a dump of the match entry in a nice format
395 // Dumping entry <handle>
396 // Match key:
397 // param_1
398 // param_2 ...
399 // [Priority: ...]
400 // Does not print anything related to the stored value
401 std::string entry_to_string(entry_handle_t handle) const;
402
403 MatchErrorCode dump_match_entry(std::ostream *out,
404 entry_handle_t handle) const;
405
406 void reset_state();
407
408 void serialize(std::ostream *out) const {
409 serialize_(out);
410 }
411
412 void deserialize(std::istream *in, const P4Objects &objs) {
413 deserialize_(in, objs);
414 }
415
416 private:
417 virtual MatchErrorCode add_entry_(const std::vector<MatchKeyParam> &match_key,
418 V value, // by value for possible std::move
419 entry_handle_t *handle,
420 int priority) = 0;
421
422 virtual MatchErrorCode delete_entry_(entry_handle_t handle) = 0;
423
424 virtual MatchErrorCode modify_entry_(entry_handle_t handle, V value) = 0;
425
426 virtual MatchErrorCode get_value_(entry_handle_t handle, const V **value) = 0;
427
428 virtual MatchErrorCode get_entry_(entry_handle_t handle,
429 std::vector<MatchKeyParam> *match_key,
430 const V **value, int *priority) const = 0;
431
432 virtual MatchErrorCode retrieve_handle_(
433 const std::vector<MatchKeyParam> &match_key,
434 entry_handle_t *handle,
435 int priority) const = 0;
436
437 virtual MatchErrorCode dump_match_entry_(std::ostream *out,
438 entry_handle_t handle) const = 0;
439
440 virtual void reset_state_() = 0;
441
442 virtual MatchUnitLookup lookup_key(const ByteContainer &key) const = 0;
443
444 virtual void serialize_(std::ostream *out) const = 0;
445 virtual void deserialize_(std::istream *in, const P4Objects &objs) = 0;
446};
447
448
449template <typename K, typename V>
450class MatchUnitGeneric : public MatchUnitAbstract<V> {
451 public:
452 using MatchUnitLookup = typename MatchUnitAbstract<V>::MatchUnitLookup;
453 struct Entry {
454 Entry() {}
455 Entry(K key, V value)
456 : key(std::move(key)), value(std::move(value)) {}
457 K key;
458 V value;
459 };
460
461 public:
462 MatchUnitGeneric(size_t size, const MatchKeyBuilder &match_key_builder,
463 LookupStructureFactory *lookup_factory)
464 : MatchUnitAbstract<V>(size, match_key_builder), entries(size),
465 lookup_structure(
466 LookupStructureFactory::create<K>(
467 lookup_factory, size, match_key_builder.get_nbytes_key())) {}
468
469 private:
470 MatchErrorCode add_entry_(const std::vector<MatchKeyParam> &match_key,
471 V value, // by value for possible std::move
472 entry_handle_t *handle,
473 int priority) override;
474
475 MatchErrorCode delete_entry_(entry_handle_t handle) override;
476
477 MatchErrorCode modify_entry_(entry_handle_t handle, V value) override;
478
479 MatchErrorCode get_value_(entry_handle_t handle, const V **value) override;
480
481 MatchErrorCode get_entry_(entry_handle_t handle,
482 std::vector<MatchKeyParam> *match_key,
483 const V **value, int *priority) const override;
484
485 MatchErrorCode retrieve_handle_(const std::vector<MatchKeyParam> &match_key,
486 entry_handle_t *handle,
487 int priority) const override;
488
489 MatchErrorCode dump_match_entry_(std::ostream *out,
490 entry_handle_t handle) const override;
491
492 void reset_state_() override;
493
494 MatchUnitLookup lookup_key(const ByteContainer &key) const override;
495
496 void serialize_(std::ostream *out) const override;
497 void deserialize_(std::istream *in, const P4Objects &objs) override;
498
499 MatchErrorCode build_entry_from_match_key(
500 const std::vector<MatchKeyParam> &match_key, int priority,
501 Entry *entry) const;
502
503 private:
504 std::vector<Entry> entries{};
505 std::unique_ptr<LookupStructure<K>> lookup_structure{nullptr};
506};
507
508// Alias all of our concrete MatchUnit types for convenience
509// when using them elsewhere.
510
511template <typename V>
512using MatchUnitExact = MatchUnitGeneric<ExactMatchKey, V>;
513
514template <typename V>
515using MatchUnitLPM = MatchUnitGeneric<LPMMatchKey, V>;
516
517template <typename V>
518using MatchUnitTernary = MatchUnitGeneric<TernaryMatchKey, V>;
519
520template <typename V>
521using MatchUnitRange = MatchUnitGeneric<RangeMatchKey, V>;
522
523
524} // namespace bm
525
526#endif // BM_BM_SIM_MATCH_UNITS_H_