58 int parse(
Args *args) {
59 double recycle_interval = 0;
63 .read_or_set_p(
"CAPACITY", _capacity, 65536)
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)
68 .read_or_set(
"RECYCLE_INTERVAL", recycle_interval, 1)
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;
83 auto passing = get_passing_threads();
84 int table_count = passing.size();
85 _capacity = next_pow2(_capacity/passing.weight());
86 _tables.compress(passing);
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;
92 for (
int ui = 0; ui < _tables.weight(); ui++) {
94 State &t = _tables.get_value(ui);
95 int core = _tables.get_mapping(ui);
97 if (((T*)
this)->alloc(t,core,errh) != 0) {
101 if (_timeout_epochs > 0) {
102 t._timer_wheel.initialize(_timeout_epochs);
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);
109 return errh->
error(
"Could not init data for table core %d!", core);
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++)
118 if (_timeout_ms > 0 && have_maintainer) {
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);
126 click_chatter(
"%p{element} does not have timeout enabled. Another mean must be used to remove old flows.",
this);
131 return Router::InitFuture::solve_initialize(errh);
134 static void run_maintain_timer(
Timer *t,
void *thunk) {
136 int core = click_current_cpu_id();
140 int removed = e->maintainer();
143 e->_tables.get_value_for_thread(core).maintain_timer->schedule_after_msec(e->_recycle_interval_ms);
146 inline void update_lastseen(FlowControlBlock *fcb,
Timestamp &ts) {
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));
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))
168 click_chatter(
"Loop detected!");
171 FlowControlBlock * next = *get_next_released_fcb(prev);
174 if (unlikely(recent <= prev->lastseen)) {
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) );
179 tw.schedule_after(prev, _timeout_epochs, setter);
183 int old = (recent - prev->lastseen).msecval();
185 if (old + _recycle_interval_ms >= _timeout_ms) {
191 if constexpr (State::need_fid()) {
194 *get_next_released_fcb(prev) = state._qbsr;
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());
208 if (likely(prev != *get_next_released_fcb(prev))) {
209 int r = (_timeout_ms) - old;
210 r = (r * (_epochs_per_sec)) / 1000;
211 tw.schedule_after(prev, r, setter);
215 click_chatter(
"Looping on the same entry. do not schedule!");
225 void push_batch(
int,
PacketBatch *batch)
override {
228 FlowControlBlock* tmp = fcb_stack;
232 FOR_EACH_PACKET_SAFE(batch, p) {
233 ((T*)
this)->process(p, b, recent);
238 if (have_maintainer && _timeout_epochs)
239 update_lastseen(fcb_stack, recent);
240 #if HAVE_FLOW_DYNAMIC
241 fcb_acquire(batch->
count());
243 output_push_batch(0, batch);
250 IPFlow5ID fid = IPFlow5ID(p);
251 if (_cache && fid == b.last_id) {
256 FlowControlBlock *fcb;
257 auto &state = *_tables;
259 int ret = ((T*)
this)->find(fid);
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!");
273 ret = ((T*)
this)->insert(fid, flowid);
277 ret = ((T*)
this)->insert(fid, 0);
282 if (unlikely(ret < 0)) {
286 fcb = get_fcb_from_flowid(ret);
289 if constexpr (State::need_fid()) {
290 *(get_fcb_flowid(fcb)) = flowid;
293 if (_timeout_epochs) {
295 state._timer_wheel.schedule_after(fcb, _timeout_epochs, setter);
301 fcb = get_fcb_from_flowid(ret);
312 if (have_maintainer && _timeout_epochs)
313 update_lastseen(fcb_stack, recent);
314 #if HAVE_FLOW_DYNAMIC
315 fcb_acquire(batch->
count());
317 output_push_batch(0, batch);
332 #define FCB_DATA(fcb, offset) (((uint8_t *)(fcb->data_32)) + (offset))
334 static inline uint32_t *get_fcb_flowid(FlowControlBlock *fcb) {
335 if constexpr (!State::need_fid()) {
338 return (uint32_t *)FCB_DATA(fcb, 0);
345 return (IPFlow5ID *)FCB_DATA(fcb, (State::need_fid()?
sizeof(uint32_t) : 0 ));
348 inline FlowControlBlock* get_fcb_from_flowid(
int i) {
349 return (FlowControlBlock *)(((uint8_t *)_tables->fcbs) +
350 _flow_state_size_full * i);
354 inline static const int reserve_size() {
355 if constexpr (State::need_fid()) {
356 return sizeof(uint32_t) +
sizeof(IPFlow5ID) +
sizeof(FlowControlBlock*);
358 return sizeof(IPFlow5ID) +
sizeof(FlowControlBlock*);
363 inline static FlowControlBlock** get_next_released_fcb(FlowControlBlock *fcb) {
364 return (FlowControlBlock**) FCB_DATA(fcb, reserve_size() -
sizeof(FlowControlBlock*));
368 const static auto constexpr setter = [](FlowControlBlock* prev, FlowControlBlock* next)
370 *(get_next_released_fcb(prev)) = next;
383 T *f =
static_cast<T *
>(e);
385 intptr_t cnt = (intptr_t)thunk;
388 return String(f->count());
390 if constexpr (State::need_fid()) {
391 return String(f->_tables->flows_stack_i);
395 return String(f->_capacity);
396 case h_total_capacity:
397 return String(f->_capacity);
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);
410 per_thread_oread<State> _tables;
413 uint32_t _flow_state_size_full;
415 uint32_t _timeout_epochs;
416 uint32_t _timeout_ms;
417 uint32_t _epochs_per_sec;
418 uint16_t _recycle_interval_ms;
419 const bool have_maintainer =
true;