bmv2
Designing your own switch target with bmv2
Loading...
Searching...
No Matches
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
49namespace bm {
50
51class 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
71class 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
196using meter_array_id_t = p4object_id_t;
197
208class 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_
Definition meters.h:208
const_iterator begin() const
NC.
Definition meters.h:283
color_t execute_meter(const Packet &pkt, size_t idx, color_t pre_color=0)
iterator end()
NC.
Definition meters.h:286
Meter & get_meter(size_t idx)
Access the meter at position idx, asserts if bad idx.
Definition meters.h:244
const Meter & get_meter(size_t idx) const
Access the meter at position idx, asserts if bad idx.
Definition meters.h:249
const_iterator end() const
NC.
Definition meters.h:289
Meter & operator[](size_t idx)
Access the meter at position idx, asserts if bad idx.
Definition meters.h:266
const Meter & at(size_t idx) const
Definition meters.h:261
size_t size() const
Returns the size of the MeterArray (i.e. number of meters it includes)
Definition meters.h:292
Meter & at(size_t idx)
Definition meters.h:256
iterator begin()
NC.
Definition meters.h:280
const Meter & operator[](size_t idx) const
Access the meter at position idx, asserts if bad idx.
Definition meters.h:272
Definition meters.h:71
color_t execute(const Packet &pkt, color_t pre_color=0)
Definition named_p4object.h:39
Definition packet.h:98