Click
Loading...
Searching...
No Matches
virtualflowmanager.hh
1#ifndef CLICK_VFLOWMANAGERIMP_HH
2#define CLICK_VFLOWMANAGERIMP_HH
3#include <click/flow/flowelement.hh>
4#include <click/timerwheel.hh>
5#include <click/batchbuilder.hh>
6#include <type_traits>
7
8class _FlowManagerIMPState { public:
9 //The table of FCBs
10 FlowControlBlock *fcbs;
11 TimerWheel<FlowControlBlock> _timer_wheel;
12 Timer* maintain_timer;
13};
14
15class FlowManagerIMPStateNoFID : public _FlowManagerIMPState { public:
16
17 static constexpr bool need_fid() {
18 return false;
19 }
20
21};
22
23
24//gtable must extends the IMPState
25class FlowManagerIMPState : public _FlowManagerIMPState { public:
26
27 // Flow stack management
28 uint32_t *flows_stack = 0;
29 int flows_stack_i;
30 FlowControlBlock* _qbsr;
31
32 static constexpr bool need_fid() {
33 return true;
34 }
35
36 inline uint32_t imp_flows_pop() {
37 return flows_stack[flows_stack_i--];
38 }
39
40 inline void imp_flows_push(uint32_t flow) {
41 flows_stack[++flows_stack_i] = flow;
42 }
43
44 inline bool imp_flows_empty() {
45 return flows_stack_i < 0;
46 }
47};
48
52template<typename T, class State>
53class VirtualFlowManagerIMP : public VirtualFlowManager, public Router::InitFuture { public:
54 VirtualFlowManagerIMP() : _tables(), _cache(true) {
55
56 };
57
58 int parse(Args *args) {
59 double recycle_interval = 0;
60 int timeout = 0;
61 int ret =
62 (*args)
63 .read_or_set_p("CAPACITY", _capacity, 65536) // HT capacity
64 .read_or_set("RESERVE", _reserve, 0)
65 .read_or_set("VERBOSE", _verbose, 0)
66 .read_or_set("CACHE", _cache, 1)
67 .read_or_set("TIMEOUT", timeout, 0) // Timeout for the entries
68 .read_or_set("RECYCLE_INTERVAL", recycle_interval, 1)
69 .consume();
70
71 _recycle_interval_ms = (int)(recycle_interval * 1000);
72 _epochs_per_sec = max(1, 1000 / _recycle_interval_ms);
73 _timeout_ms = timeout * 1000;
74 _timeout_epochs = timeout * _epochs_per_sec;
75
76 //_reserve += reserve_size() is called by the parrent
77
78 return ret;
79 }
80
81 int solve_initialize(ErrorHandler *errh) override
82 {
83 auto passing = get_passing_threads();
84 int table_count = passing.size();
85 _capacity = next_pow2(_capacity/passing.weight());
86 _tables.compress(passing);
87
88 click_chatter("Real capacity for each table will be %d", _capacity);
89 assert(_reserve >= reserve_size());
90 _flow_state_size_full = sizeof(FlowControlBlock) + _reserve;
91
92 for (int ui = 0; ui < _tables.weight(); ui++) {
93
94 State &t = _tables.get_value(ui);
95 int core = _tables.get_mapping(ui);
96
97 if (((T*)this)->alloc(t,core,errh) != 0) {
98 return -1;
99 }
100
101 if (_timeout_epochs > 0) {
102 t._timer_wheel.initialize(_timeout_epochs);
103 }
104
105 t.fcbs = (FlowControlBlock*)CLICK_ALIGNED_ALLOC(_flow_state_size_full * _capacity);
106 CLICK_ASSERT_ALIGNED(t.fcbs);
107 bzero(t.fcbs,_flow_state_size_full * _capacity);
108 if (!t.fcbs) {
109 return errh->error("Could not init data for table core %d!", core);
110 }
111
112 if constexpr (State::need_fid()) {
113 t.flows_stack = (uint32_t *)CLICK_ALIGNED_ALLOC(sizeof(uint32_t) * (_capacity + 1));
114 for (int i =0; i < _capacity; i++)
115 t.imp_flows_push(i);
116 }
117
118 if (_timeout_ms > 0 && have_maintainer) {
119 //click_chatter("Initializing maintain timer %d", core);
120 t.maintain_timer = new Timer(this);
121 t.maintain_timer->initialize(this, true);
122 t.maintain_timer->assign(run_maintain_timer, this);
123 t.maintain_timer->move_thread(core);
124 t.maintain_timer->schedule_after_msec(_recycle_interval_ms);
125 } else {
126 click_chatter("%p{element} does not have timeout enabled. Another mean must be used to remove old flows.", this);
127 }
128
129 }
130
131 return Router::InitFuture::solve_initialize(errh);
132 }
133
134 static void run_maintain_timer(Timer *t, void *thunk) {
135
136 int core = click_current_cpu_id();
138 reinterpret_cast<VirtualFlowManagerIMP *>(thunk);
139
140 int removed = e->maintainer();
141 //click_chatter("Run timer, removed %d", removed);
142
143 e->_tables.get_value_for_thread(core).maintain_timer->schedule_after_msec(e->_recycle_interval_ms);
144 }
145
146 inline void update_lastseen(FlowControlBlock *fcb, Timestamp &ts) {
147 fcb->lastseen = ts;
148 }
149
150
151 inline int maintainer() {
153 int dest_core = click_current_cpu_id();
154 State &state = *_tables;
155 if constexpr (State::need_fid()) {
156 while (state._qbsr) {
157 FlowControlBlock* next = *get_next_released_fcb(state._qbsr);
158 state.imp_flows_push(*get_fcb_flowid(state._qbsr));
159 state._qbsr = next;
160 }
161 }
162
163 int checker = 0;
164 TimerWheel<FlowControlBlock> &tw = state._timer_wheel;
165 tw.run_timers([this,recent,&checker, &tw, &state, dest_core](FlowControlBlock* prev) -> FlowControlBlock* {
166 if (unlikely(checker >= _capacity))
167 {
168 click_chatter("Loop detected!");
169 abort();
170 }
171 FlowControlBlock * next = *get_next_released_fcb(prev);
172 //Verify lastseen is not in the future
173 // click_chatter("%d %d, next %p", recent, prev->lastseen, next);
174 if (unlikely(recent <= prev->lastseen)) {
175
176 int64_t old = (recent - prev->lastseen).msecval();
177 click_chatter("Old %li : %s %s fid %d",old, recent.unparse().c_str(), prev->lastseen.unparse().c_str(),get_fcb_flowid(prev) );
178
179 tw.schedule_after(prev, _timeout_epochs, setter);
180 return next;
181 }
182
183 int old = (recent - prev->lastseen).msecval();
184
185 if (old + _recycle_interval_ms >= _timeout_ms) {
186
187 //expire
188 //click_chatter("Release %p", prev);
189
190 int pos = ((T*)this)->remove(*get_fcb_key(prev));
191 if constexpr (State::need_fid()) {
192 if (likely(pos==0))
193 {
194 *get_next_released_fcb(prev) = state._qbsr;
195 state._qbsr = prev;
196 }
197 else
198 {
199 click_chatter("[%d->%d] error %d Removed a key not in the table flow %d (%s)...", click_current_cpu_id(), dest_core , pos , *get_fcb_flowid(prev), get_fcb_key(prev)->unparse().c_str());
200 }
201 } else {
202
203 }
204
205 checker++;
206 } else {
207 //No need for lock as we'll be the only one to enqueue there
208 if (likely(prev != *get_next_released_fcb(prev))) {
209 int r = (_timeout_ms) - old; //Time left in ms
210 r = (r * (_epochs_per_sec)) / 1000;
211 tw.schedule_after(prev, r, setter);
212 }
213 else
214 {
215 click_chatter("Looping on the same entry. do not schedule!");
216 abort();
217 }
218 }
219 return next;
220 });
221
222 return checker;
223 }
224
225 void push_batch(int, PacketBatch *batch) override {
226 BatchBuilder b;
227 Timestamp recent;
228 FlowControlBlock* tmp = fcb_stack;
229 if (have_maintainer)
230 recent = Timestamp::recent_steady();
231
232 FOR_EACH_PACKET_SAFE(batch, p) {
233 ((T*)this)->process(p, b, recent);
234 }
235
236 batch = b.finish();
237 if (batch) {
238 if (have_maintainer && _timeout_epochs)
239 update_lastseen(fcb_stack, recent);
240 #if HAVE_FLOW_DYNAMIC
241 fcb_acquire(batch->count());
242 #endif
243 output_push_batch(0, batch);
244 }
245 fcb_stack = tmp;
246 }
247
248
249inline void process(Packet *p, BatchBuilder &b, Timestamp &recent) {
250 IPFlow5ID fid = IPFlow5ID(p);
251 if (_cache && fid == b.last_id) {
252 b.append(p);
253 return;
254 }
255
256 FlowControlBlock *fcb;
257 auto &state = *_tables;
258
259 int ret = ((T*)this)->find(fid);
260
261 if (ret <= 0) {
262 uint32_t flowid;
263 if constexpr (State::need_fid()) {
264 flowid = state.imp_flows_pop();
265 if (unlikely(flowid == 0)) {
266 click_chatter("ID is 0 and table is full!");
267 p->kill();
268 return;
269 }
270
271 // INSERT IN TABLE
272
273 ret = ((T*)this)->insert(fid, flowid);
274
275 } else {
276
277 ret = ((T*)this)->insert(fid, 0);
278
279 flowid = ret;
280 }
281
282 if (unlikely(ret < 0)) {
283 p->kill();
284 return;
285 }
286 fcb = get_fcb_from_flowid(ret);
287
288
289 if constexpr (State::need_fid()) {
290 *(get_fcb_flowid(fcb)) = flowid;
291 }
292
293 if (_timeout_epochs) {
294 memcpy(get_fcb_key(fcb), &fid, sizeof(IPFlow5ID));
295 state._timer_wheel.schedule_after(fcb, _timeout_epochs, setter);
296 }
297
298 } // (end)It's a new flow
299 else
300 { // Old flow
301 fcb = get_fcb_from_flowid(ret);
302 }
303
304 if (b.last == ret) {
305 b.append(p);
306 } else {
307 PacketBatch *batch;
308 batch = b.finish();
309
310
311 if (batch) {
312 if (have_maintainer && _timeout_epochs)
313 update_lastseen(fcb_stack, recent);
314 #if HAVE_FLOW_DYNAMIC
315 fcb_acquire(batch->count());
316 #endif
317 output_push_batch(0, batch);
318 }
319 fcb_stack = fcb;
320 b.init();
321 b.append(p);
322 b.last = ret;
323 if (_cache) {
324 b.last_id = fid;
325 }
326 }
327}
328
329
330protected:
331
332 #define FCB_DATA(fcb, offset) (((uint8_t *)(fcb->data_32)) + (offset))
333
334 static inline uint32_t *get_fcb_flowid(FlowControlBlock *fcb) {
335 if constexpr (!State::need_fid()) {
336 assert(false);
337 }
338 return (uint32_t *)FCB_DATA(fcb, 0);
339 };
340
344 static inline IPFlow5ID *get_fcb_key(FlowControlBlock *fcb) {
345 return (IPFlow5ID *)FCB_DATA(fcb, (State::need_fid()? sizeof(uint32_t) : 0 ));
346 };
347
348 inline FlowControlBlock* get_fcb_from_flowid(int i) {
349 return (FlowControlBlock *)(((uint8_t *)_tables->fcbs) +
350 _flow_state_size_full * i);
351 }
352
353
354 inline static const int reserve_size() {
355 if constexpr (State::need_fid()) {
356 return sizeof(uint32_t) + sizeof(IPFlow5ID) + sizeof(FlowControlBlock*);
357 } else {
358 return sizeof(IPFlow5ID) + sizeof(FlowControlBlock*);
359 }
360
361 }
362
363 inline static FlowControlBlock** get_next_released_fcb(FlowControlBlock *fcb) {
364 return (FlowControlBlock**) FCB_DATA(fcb, reserve_size() - sizeof(FlowControlBlock*));
365 };
366
367
368 const static auto constexpr setter = [](FlowControlBlock* prev, FlowControlBlock* next)
369 {
370 *(get_next_released_fcb(prev)) = next;
371 };
372
373
374
375 enum {
376 h_count,
377 h_count_fids,
378 h_capacity,
379 h_total_capacity
380 };
381
382 static String read_handler(Element *e, void *thunk) {
383 T *f = static_cast<T *>(e);
384
385 intptr_t cnt = (intptr_t)thunk;
386 switch (cnt) {
387 case h_count:
388 return String(f->count());
389 case h_count_fids:
390 if constexpr (State::need_fid()) {
391 return String(f->_tables->flows_stack_i);
392
393 } else return 0;
394 case h_capacity:
395 return String(f->_capacity);
396 case h_total_capacity:
397 return String(f->_capacity);
398 default:
399 return "<error>";
400 }
401 }
402
403 void add_handlers() {
404 add_read_handler("count", read_handler, h_count);
405 add_read_handler("count_fids", read_handler, h_count_fids);
406 add_read_handler("capacity", read_handler, h_capacity);
407 add_read_handler("total_capacity", read_handler, h_total_capacity);
408 }
409
410 per_thread_oread<State> _tables;
411 uint32_t _capacity;
412 int _verbose;
413 uint32_t _flow_state_size_full;
414 bool _cache;
415 uint32_t _timeout_epochs;
416 uint32_t _timeout_ms; // Timeout for deletion
417 uint32_t _epochs_per_sec; // Granularity for the epoch
418 uint16_t _recycle_interval_ms; // When to run the maintainer
419 const bool have_maintainer = true;
420};
421
422#endif
Argument parser class.
Definition args.hh:249
Base class for Click elements.
Definition element.hh:42
Error reporting class.
Definition error.hh:90
int error(const char *fmt,...)
Print an error message (level el_error).
Definition error.cc:825
Definition packetbatch.hh:415
unsigned count()
Definition packetbatch.hh:475
A network packet.
Definition packet.hh:68
void kill()
Delete this packet.
Definition packet.hh:1804
Definition router.hh:160
A string of characters.
Definition string.hh:22
const char * c_str() const
Null-terminate the string.
Definition string.hh:519
Triggers execution at a given time.
Definition timer.hh:14
Represents a moment or interval in time.
Definition timestamp.hh:125
static Timestamp recent_steady()
Return a recent steady-clock time.
Definition timestamp.hh:1000
String unparse() const
Unparse this timestamp into a String.
Definition timestamp.cc:240
Definition virtualflowmanager.hh:53
static IPFlow5ID * get_fcb_key(FlowControlBlock *fcb)
Definition virtualflowmanager.hh:344