bmv2
Designing your own switch target with bmv2
meters.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 
34 
35 #ifndef BM_BM_SIM_METERS_H_
36 #define BM_BM_SIM_METERS_H_
37 
38 #include <vector>
39 #include <mutex>
40 #include <memory>
41 #include <string>
42 #include <iosfwd>
43 #include <algorithm>
44 
45 #include <cassert>
46 
47 #include "named_p4object.h"
48 
49 namespace bm {
50 
51 class Packet;
52 
53 // I initially implemented this with template values: meter type and rate
54 // count. I thought it would potentially speed up operations. However, it meant
55 // I also had to use a virtual interface (e.g. to store in p4 objects / use in
56 // action primitives). After some separate benchmarking, I decided against using
57 // templates (not really worth it). The only time I observed a real speed up was
58 // without using virtual functions (which is not really possible here anyway)
59 // and for a certain rate count (for which the compiler was doing loop
60 // unrolling, probably was a more cache friendly value too). I think I can live
61 // with the extra overhead of having a vector (vs an array) and having to test
62 // for the meter type. Maybe I will change this later, but meters are not used
63 // that much so for now I am going for simplicity.
64 
71 class Meter {
72  public:
73  using color_t = unsigned int;
74  using rate_idx_t = size_t;
75  struct rate_config_t {
76  double info_rate;
77  size_t burst_size;
78 
79  static rate_config_t make(double info_rate, size_t burst_size) {
80  return {info_rate, burst_size};
81  }
82  };
83  using clock = std::chrono::steady_clock;
84 
85  public:
86  enum class MeterType {
87  BYTES,
88  PACKETS
89  };
90 
91  enum MeterErrorCode {
92  SUCCESS = 0,
93  INVALID_METER_NAME,
94  INVALID_INDEX,
95  BAD_RATES_LIST,
96  INVALID_INFO_RATE_VALUE,
97  INVALID_BURST_SIZE_VALUE,
98  ERROR
99  };
100 
101  public:
102  Meter(MeterType type, size_t rate_count)
103  : type(type), rates(rate_count) { }
104 
105  // the rate configs must be sorted from smaller rate to higher rate
106  // in the 2 rate meter case: {CIR, PIR}
107 
108  // this is probably a total overkill, but I am entitled to my fun. set_rates
109  // accepts a vector, an initializer list, or any Random Accces Iterator pair.
110  template<typename RAIt>
111  MeterErrorCode set_rates(const RAIt first, const RAIt last) {
112  // I think using static asserts is cleaner than using SFINAE / enable_if
113  static_assert(
114  std::is_same<std::random_access_iterator_tag,
115  typename std::iterator_traits<RAIt>::iterator_category>::value,
116  "wrong iterator category");
117  static_assert(
118  std::is_same<rate_config_t,
119  typename std::iterator_traits<RAIt>::value_type>::value,
120  "wrong iterator value type");
121  typename std::iterator_traits<RAIt>::difference_type n =
122  std::distance(first, last);
123  assert(n >= 0);
124  auto lock = unique_lock();
125  if (static_cast<size_t>(n) != rates.size()) return BAD_RATES_LIST;
126  size_t idx = 0;
127  for (auto it = first; it < last; ++it) {
128  MeterErrorCode rc = set_rate(idx++, *it);
129  if (rc != SUCCESS) return rc;
130  }
131  std::reverse(rates.begin(), rates.end());
132  configured = true;
133  return SUCCESS;
134  }
135 
136  MeterErrorCode set_rates(const std::vector<rate_config_t> &configs) {
137  return set_rates(configs.begin(), configs.end());
138  }
139 
140  MeterErrorCode set_rates(
141  const std::initializer_list<rate_config_t> &configs) {
142  return set_rates(configs.begin(), configs.end());
143  }
144 
145  // returns an empty vector if meter not configured
146  std::vector<rate_config_t> get_rates() const;
147 
148  MeterErrorCode reset_rates();
149 
158  color_t execute(const Packet &pkt, color_t pre_color = 0);
159 
160  void serialize(std::ostream *out) const;
161  void deserialize(std::istream *in);
162 
163  public:
164  /* This is for testing purposes only, for more accurate tests */
165  static void reset_global_clock();
166 
167  private:
168  using UniqueLock = std::unique_lock<std::mutex>;
169  UniqueLock unique_lock() const { return UniqueLock(*m_mutex); }
170  void unlock(UniqueLock &lock) const { lock.unlock(); } // NOLINT
171 
172  private:
173  struct MeterRate {
174  bool valid{}; // TODO(antonin): get rid of this?
175  double info_rate{}; // in bytes / packets per microsecond
176  size_t burst_size{};
177  size_t tokens{};
178  uint64_t tokens_last{};
179  color_t color{};
180  };
181 
182  private:
183  MeterErrorCode set_rate(size_t idx, const rate_config_t &config);
184 
185  private:
186  MeterType type;
187  // I decided to take the easy route and wrap the m_mutex into a
188  // unique_ptr. Mutexes are not movable and that would be a problem with the
189  // MeterArray implementation. I don't think this will incur a performance hit
190  std::unique_ptr<std::mutex> m_mutex{new std::mutex()};
191  // mutable std::mutex m_mutex;
192  std::vector<MeterRate> rates;
193  bool configured{false};
194 };
195 
196 using meter_array_id_t = p4object_id_t;
197 
208 class MeterArray : public NamedP4Object {
209  public:
210  using MeterErrorCode = Meter::MeterErrorCode;
211  using color_t = Meter::color_t;
212  using MeterType = Meter::MeterType;
213  using rate_config_t = Meter::rate_config_t;
214 
215  using iterator = std::vector<Meter>::iterator;
216  using const_iterator = std::vector<Meter>::const_iterator;
217 
218  public:
219  MeterArray(const std::string &name, p4object_id_t id,
220  MeterType type, size_t rate_count, size_t size);
221 
225  color_t execute_meter(const Packet &pkt, size_t idx, color_t pre_color = 0);
226 
227  template<class RAIt>
228  MeterErrorCode set_rates(const RAIt first, const RAIt last) {
229  // check validity of rates here?
230  MeterErrorCode rc;
231  for (Meter &m : meters) {
232  rc = m.set_rates(first, last);
233  if (rc != MeterErrorCode::SUCCESS) return rc;
234  }
235  return MeterErrorCode::SUCCESS;
236  }
237 
238  MeterErrorCode set_rates(const std::vector<rate_config_t> &configs);
239 
240  MeterErrorCode set_rates(
241  const std::initializer_list<rate_config_t> &configs);
242 
244  Meter &get_meter(size_t idx) {
245  return meters[idx];
246  }
247 
249  const Meter &get_meter(size_t idx) const {
250  return meters[idx];
251  }
252 
256  Meter &at(size_t idx) {
257  return meters.at(idx);
258  }
259 
261  const Meter &at(size_t idx) const {
262  return meters.at(idx);
263  }
264 
266  Meter &operator[](size_t idx) {
267  assert(idx < size());
268  return meters[idx];
269  }
270 
272  const Meter &operator[](size_t idx) const {
273  assert(idx < size());
274  return meters[idx];
275  }
276 
277  // iterators
278 
280  iterator begin() { return meters.begin(); }
281 
283  const_iterator begin() const { return meters.begin(); }
284 
286  iterator end() { return meters.end(); }
287 
289  const_iterator end() const { return meters.end(); }
290 
292  size_t size() const { return meters.size(); }
293 
294  void reset_state();
295 
296  void serialize(std::ostream *out) const;
297  void deserialize(std::istream *in);
298 
299  private:
300  std::vector<Meter> meters{};
301 };
302 
303 } // namespace bm
304 
305 #endif // BM_BM_SIM_METERS_H_
bm::NamedP4Object
Definition: named_p4object.h:39
bm::MeterArray::at
Meter & at(size_t idx)
Definition: meters.h:256
bm::MeterArray
Definition: meters.h:208
bm::MeterArray::get_meter
Meter & get_meter(size_t idx)
Access the meter at position idx, asserts if bad idx.
Definition: meters.h:244
bm::MeterArray::execute_meter
color_t execute_meter(const Packet &pkt, size_t idx, color_t pre_color=0)
bm::MeterArray::end
iterator end()
NC.
Definition: meters.h:286
bm::MeterArray::begin
const_iterator begin() const
NC.
Definition: meters.h:283
bm::MeterArray::end
const_iterator end() const
NC.
Definition: meters.h:289
bm::Meter::execute
color_t execute(const Packet &pkt, color_t pre_color=0)
bm::MeterArray::operator[]
Meter & operator[](size_t idx)
Access the meter at position idx, asserts if bad idx.
Definition: meters.h:266
bm::MeterArray::size
size_t size() const
Returns the size of the MeterArray (i.e. number of meters it includes)
Definition: meters.h:292
bm::MeterArray::get_meter
const Meter & get_meter(size_t idx) const
Access the meter at position idx, asserts if bad idx.
Definition: meters.h:249
named_p4object.h
bm::Meter
Definition: meters.h:71
bm::Packet
Definition: packet.h:98
bm::MeterArray::at
const Meter & at(size_t idx) const
Definition: meters.h:261
bm::MeterArray::begin
iterator begin()
NC.
Definition: meters.h:280
bm::MeterArray::operator[]
const Meter & operator[](size_t idx) const
Access the meter at position idx, asserts if bad idx.
Definition: meters.h:272