bmv2
Designing your own switch target with bmv2
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 
44 namespace bm {
45 
46 class P4Objects; // forward declaration for deserialize
47 
48 // using string and not ByteContainer for efficiency
49 struct 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 
83 namespace detail {
84 
85 class 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.
91 class 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 
170 namespace MatchUnit {
171 
172 struct 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 
213 struct 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 
229 class 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 
350 template <typename V>
351 class 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 
449 template <typename K, typename V>
450 class 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 
511 template <typename V>
512 using MatchUnitExact = MatchUnitGeneric<ExactMatchKey, V>;
513 
514 template <typename V>
515 using MatchUnitLPM = MatchUnitGeneric<LPMMatchKey, V>;
516 
517 template <typename V>
518 using MatchUnitTernary = MatchUnitGeneric<TernaryMatchKey, V>;
519 
520 template <typename V>
521 using MatchUnitRange = MatchUnitGeneric<RangeMatchKey, V>;
522 
523 
524 } // namespace bm
525 
526 #endif // BM_BM_SIM_MATCH_UNITS_H_
lookup_structures.h
bytecontainer.h
counters.h
meters.h
packet.h