bmv2
Designing your own switch target with bmv2
calculations.h
Go to the documentation of this file.
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 (antonin@barefootnetworks.com)
18  *
19  */
20 
30 
33 
52 
53 
54 #ifndef BM_BM_SIM_CALCULATIONS_H_
55 #define BM_BM_SIM_CALCULATIONS_H_
56 
57 #include <algorithm> // for std::copy
58 #include <iosfwd>
59 #include <memory>
60 #include <string>
61 #include <tuple>
62 #include <type_traits>
63 #include <unordered_map>
64 #include <utility>
65 #include <vector>
66 
67 #include "boost/variant.hpp"
68 
69 #include "bytecontainer.h"
70 #include "named_p4object.h"
71 #include "phv_forward.h"
72 
73 namespace bm {
74 
75 class Packet;
76 
77 /* Used to determine whether a class overloads the '()' operator */
78 template <typename T>
79 struct defines_functor_operator {
80  using yes = char (&)[1];
81  using no = char (&)[2];
82 
83  // we need a template here to enable SFINAE
84  template <typename U>
85  static yes deduce(char (*)[sizeof(&U::operator())]);
86  // fallback
87  template <typename> static no deduce(...);
88 
89  static bool constexpr value = sizeof(deduce<T>(0)) == sizeof(yes);
90 };
91 
92 namespace detail {
93 
94 template <class ReturnType, class... Args>
95 struct callable_traits_base {
96  using return_type = ReturnType;
97  using argument_type = std::tuple<Args...>;
98 
99  template<std::size_t I>
100  using arg = typename std::tuple_element<I, argument_type>::type;
101 };
102 
103 } // namespace detail
104 
105 /* Used to determine the return type and argument type of the '()' operator */
106 template <class T>
107 struct callable_traits : callable_traits<decltype(&T::operator())> { };
108 
109 /* lambda / functor */
110 template <class ClassType, class ReturnType, class... Args>
111 struct callable_traits<ReturnType(ClassType::*)(Args...) const>
112  : detail::callable_traits_base<ReturnType, Args...> { };
113 
114 /* non-const case */
115 template <class ClassType, class ReturnType, class... Args>
116 struct callable_traits<ReturnType(ClassType::*)(Args...)>
117  : detail::callable_traits_base<ReturnType, Args...> { };
118 
119 template <bool functor, typename H>
120 struct HashChecker {
121  protected:
122  static bool constexpr valid_hash = false;
123 
124  ~HashChecker() { }
125 };
126 
127 /* specific to hash algorithms
128  checks that the signature of the '()' operator is what we want */
129 template<typename T, typename Return>
130 struct check_functor_signature {
131  template<typename U, Return (U::*)(const char *, size_t) const>
132  struct SFINAE {};
133 
134  template<typename U> static char Test(SFINAE<U, &U::operator()>*);
135  template<typename U> static int Test(...);
136  static const bool value = sizeof(Test<T>(0)) == sizeof(char);
137 };
138 
139 /* provides "readable" error messages if an hash functor was not define
140  correctly */
141 template <typename H>
142 struct HashChecker<true, H> {
143  private:
144  using return_type = typename callable_traits<H>::return_type;
145  using argument_type = typename callable_traits<H>::argument_type;
146 
147  static bool constexpr v1 = std::is_unsigned<return_type>::value;
148  static bool constexpr v2 =
149  std::is_same<argument_type, std::tuple<const char *, size_t>>::value;
150 
151  static_assert(v1, "Invalid return type for HashFn");
152  static_assert(v2, "Invalid parameters for HashFn");
153 
154  static bool constexpr v3 = check_functor_signature<H, return_type>::value;
155  static_assert(!(v1 && v2) || v3, "HashFn is not const");
156 
157  protected:
158  static bool constexpr valid_hash = v1 && v2 && v3;
159 
160  ~HashChecker() { }
161 };
162 
163 
164 template <typename U,
165  typename std::enable_if<std::is_unsigned<U>::value, int>::type = 0>
166 class RawCalculationIface {
167  public:
168  U output(const char *buffer, size_t s) const {
169  return output_(buffer, s);
170  }
171 
172  std::unique_ptr<RawCalculationIface<U> > clone() const {
173  return std::unique_ptr<RawCalculationIface<U> > (clone_());
174  }
175 
176  virtual ~RawCalculationIface() { }
177 
178  private:
179  virtual U output_(const char *buffer, size_t s) const = 0;
180 
181  virtual RawCalculationIface<U> *clone_() const = 0;
182 };
183 
184 
185 template <typename T, typename HashFn,
186  typename std::enable_if<std::is_unsigned<T>::value, int>::type = 0>
187 class RawCalculation
188  : HashChecker<defines_functor_operator<HashFn>::value, HashFn>,
189  public RawCalculationIface<T> {
190  public:
191  // explicit RawCalculation(const HashFn &hash) : hash(hash) { }
192 
193  std::unique_ptr<RawCalculation<T, HashFn> > clone() const {
194  return std::unique_ptr<RawCalculation<T, HashFn> > (clone_());
195  }
196 
197  HashFn &get_hash_fn() { return hash; }
198 
199  private:
200  T output_(const char *buffer, size_t s) const override {
201  return output__(buffer, s);
202  }
203 
204  RawCalculation<T, HashFn> *clone_() const override {
205  RawCalculation<T, HashFn> *ptr = new RawCalculation<T, HashFn>();
206  return ptr;
207  }
208 
209  using HC = HashChecker<defines_functor_operator<HashFn>::value, HashFn>;
210 
211  static_assert(defines_functor_operator<HashFn>::value,
212  "HashFn needs to overload '()' operator");
213 
214  template <typename U = T,
215  typename std::enable_if<std::is_unsigned<U>::value, int>::type = 0>
216  typename std::enable_if<HC::valid_hash, U>::type
217  output__(const char *buffer, size_t s) const {
218  return static_cast<U>(hash(buffer, s));
219  }
220 
221  template <typename U = T,
222  typename std::enable_if<std::is_unsigned<U>::value, int>::type = 0>
223  typename std::enable_if<!HC::valid_hash, U>::type
224  output__(const char *buffer, size_t s) const {
225  (void) buffer; (void) s;
226  return static_cast<U>(0u);
227  }
228 
229  HashFn hash;
230 };
231 
232 
233 class BufBuilder {
234  public:
235  void push_back_field(header_id_t header, int field_offset);
236  void push_back_constant(const ByteContainer &v, size_t nbits);
237  void push_back_header(header_id_t header);
238  void append_payload();
239 
240  void operator()(const Packet &pkt, ByteContainer *buf) const;
241 
242  private:
243  struct field_t {
244  header_id_t header;
245  int field_offset;
246  };
247 
248  struct constant_t {
249  ByteContainer v;
250  size_t nbits;
251  };
252 
253  struct header_t {
254  header_id_t header;
255  };
256 
257  struct Deparse; // defined in calculations.cpp
258 
259  std::vector<boost::variant<field_t, constant_t, header_t> > entries{};
260  bool with_payload{false};
261 };
262 
263 
264 template <typename T,
265  typename std::enable_if<std::is_unsigned<T>::value, int>::type = 0>
266 class Calculation_ {
267  public:
268  Calculation_(const BufBuilder &builder,
269  std::unique_ptr<RawCalculationIface<T> > c)
270  : builder(builder), c(std::move(c)) { }
271 
272  T output(const Packet &pkt) const {
273  static thread_local ByteContainer key;
274  builder(pkt, &key);
275  return c->output(key.data(), key.size());
276  }
277 
278  RawCalculationIface<T> *get_raw_calculation() { return c.get(); }
279 
280  protected:
281  ~Calculation_() { }
282 
283  private:
284  BufBuilder builder;
285  std::unique_ptr<RawCalculationIface<T> > c;
286 };
287 
288 
289 class CalculationsMap {
290  public:
291  using MyC = RawCalculationIface<uint64_t>;
292 
293  static CalculationsMap *get_instance();
294  bool register_one(const char *name, std::unique_ptr<MyC> c);
295  std::unique_ptr<MyC> get_copy(const std::string &name);
296 
297  private:
298  std::unordered_map<std::string, std::unique_ptr<MyC> > map_{};
299 };
300 
301 
302 class Calculation : public Calculation_<uint64_t> {
303  public:
304  Calculation(const BufBuilder &builder,
305  std::unique_ptr<RawCalculationIface<uint64_t> > c)
306  : Calculation_(builder, std::move(c)) { }
307 
308  Calculation(const BufBuilder &builder, const std::string &hash_name)
309  : Calculation_(
310  builder, CalculationsMap::get_instance()->get_copy(hash_name)
311  ) { }
312 };
313 
314 
315 class NamedCalculation : public NamedP4Object, public Calculation_<uint64_t> {
316  public:
317  NamedCalculation(const std::string &name, p4object_id_t id,
318  const BufBuilder &builder,
319  std::unique_ptr<RawCalculationIface<uint64_t> > c)
320  : NamedP4Object(name, id),
321  Calculation_(builder, std::move(c)) { }
322 
323  NamedCalculation(const std::string &name, p4object_id_t id,
324  const BufBuilder &builder, const std::string &hash_name)
325  : NamedP4Object(name, id),
326  Calculation_(
327  builder, CalculationsMap::get_instance()->get_copy(hash_name)
328  ) { }
329 };
330 
331 
336 #define REGISTER_HASH(hash_name) \
337  bool hash_name##_create_ = \
338  bm::CalculationsMap::get_instance()->register_one( \
339  #hash_name, \
340  std::unique_ptr<bm::CalculationsMap::MyC>( \
341  new bm::RawCalculation<uint64_t, hash_name>()));
342 
343 
344 namespace hash {
345 
346 uint64_t xxh64(const char *buffer, size_t s);
347 
348 } // namespace hash
349 
350 
351 enum class CustomCrcErrorCode {
352  SUCCESS = 0,
353  INVALID_CALCULATION_NAME,
354  WRONG_TYPE_CALCULATION,
355  INVALID_CONFIG,
356 };
357 
358 namespace detail {
359 
360 template <typename T>
361 struct crc_config_t {
362  T polynomial;
363  T initial_remainder;
364  T final_xor_value;
365  bool data_reflected;
366  bool remainder_reflected;
367 };
368 
369 template <typename T>
370 std::ostream &operator<<(std::ostream &out, const crc_config_t<T> &c);
371 
372 } // namespace detail
373 
374 // can be used for crc8, with T == uint8_t
375 // can be used for crc16, with T == uint16_t
376 // can be used for crc32, with T == uint32_t
377 // can be used for crc64, with T == uint64_t
378 template <typename T>
379 class CustomCrcMgr {
380  public:
381  using crc_config_t = detail::crc_config_t<T>;
382 
383  static CustomCrcErrorCode update_config(NamedCalculation *calculation,
384  const crc_config_t &config);
385 
386  static CustomCrcErrorCode update_config(RawCalculationIface<uint64_t> *c,
387  const crc_config_t &config);
388 };
389 
390 enum class ToeplitzErrorCode {
391  SUCCESS = 0,
392  INVALID_CALCULATION_NAME,
393  WRONG_TYPE_CALCULATION,
394  INVALID_KEY,
395 };
396 
397 class ToeplitzMgr {
398  public:
399  using key_t = ByteContainer;
400 
401  static ToeplitzErrorCode update_key(NamedCalculation *calculation,
402  const key_t &key);
403 
404  static ToeplitzErrorCode update_key(RawCalculationIface<uint64_t> *c,
405  const key_t &key);
406 };
407 
408 } // namespace bm
409 
410 #endif // BM_BM_SIM_CALCULATIONS_H_
bytecontainer.h
named_p4object.h