libTFO
|
00001 00030 #ifndef __TFO_HH__ 00031 #define __TFO_HH__ 00032 00033 #include <libtfo/fibentry.hh> 00034 00035 #include <stdint.h> 00036 #include <set> 00037 #include <list> 00038 #include <ext/hash_map> 00039 00040 using namespace std; 00041 using namespace __gnu_cxx; 00042 00043 enum FibEntryUpdateType 00044 { 00045 FIB_ENTRY_ADD, 00046 FIB_ENTRY_REMOVE, 00047 FIB_ENTRY_MODIFY 00048 }; 00049 00061 template <typename O, 00062 typename M, typename A, typename U, 00063 typename MHashFcn = hash<const M*>, 00064 typename MEqualFcn = equal_to<const M*>, 00065 typename AEqualFcn = equal_to<const A> 00066 > 00067 class TFO 00068 { 00069 public: 00070 typedef O owner_type; 00071 typedef M match_type; 00072 typedef A action_type; 00073 typedef U user_type; 00074 typedef void (*fib_modify_func) (O owner, FibEntry<M, A, U>* e); 00075 typedef void (*walk_const_callback_func) (O owner, const FibEntry<M, A, U>* e, void* user_data); 00076 typedef void (*walk_callback_func) (O owner, FibEntry<M, A, U>* e, void* user_data); 00077 00087 inline TFO (O owner, 00088 const uint32_t fib_capacity, const fib_modify_func fib_modify, 00089 const uint32_t size_b2, const uint32_t size_b3) 00090 : _owner(owner), 00091 _fib_capacity(fib_capacity), _fib_modify(fib_modify), 00092 _size_b2(size_b2), _size_b3(size_b3) 00093 { 00094 } 00095 00099 inline ~TFO (void) 00100 { 00101 for (typename _fib_hash_type::iterator i = _fib_entries.begin(); 00102 i != _fib_entries.end(); 00103 ) 00104 { 00105 FibEntry<M, A, U>* e = i->second; 00106 typename _fib_hash_type::iterator i_next = i; 00107 i_next++; 00108 _fib_entries.erase(i); 00109 i = i_next; 00110 delete e; 00111 } 00112 } 00113 00125 inline int update_entry (const M& m, const A& a, const enum FibEntryUpdateType type) 00126 { 00127 typename _fib_hash_type::iterator i = _fib_entries.find(&m); 00128 00129 switch (type) 00130 { 00131 case FIB_ENTRY_ADD: 00132 case FIB_ENTRY_MODIFY: 00133 { 00134 // Check if entry is already there. 00135 if (i != _fib_entries.end()) 00136 { 00137 FibEntry<M, A, U>* e = i->second; 00138 00139 if (e->state() == FIB_ENTRY_STATE_INACTIVE) 00140 { 00141 // Entry is inactive, activate it. 00142 e->action(a); // Update action, it might have changed. 00143 e->old_action(a); // Also update old_action. 00144 e->table(FIB_ENTRY_TABLE_SLOW); 00145 _do_fib_modify(e, FIB_ENTRY_STATE_MOD_ADD); 00146 } 00147 else if (!_action_equal(e->action(), a)) 00148 { 00149 // Action is different, modify it. 00150 e->action(a); 00151 _do_fib_modify(e, FIB_ENTRY_STATE_MOD_ACTION); 00152 } 00153 00154 return 0; // Existing entry modified, or similar existing active entry found. 00155 } 00156 00157 // Insert new entry. 00158 FibEntry<M, A, U>* e = new FibEntry<M, A, U>(_size_b2, _size_b3, m, a); 00159 _fib_entries[&e->match()] = e; 00160 00161 // New entries initially go into slow path. 00162 e->table(FIB_ENTRY_TABLE_SLOW); 00163 _do_fib_modify(e, FIB_ENTRY_STATE_MOD_ADD); 00164 return 0; // New entry installed. 00165 } 00166 break; 00167 case FIB_ENTRY_REMOVE: 00168 { 00169 if (i == _fib_entries.end()) 00170 return -1; // Entry not found. 00171 00172 FibEntry<M, A, U>* e = i->second; 00173 if (!_action_equal(e->action(), a)) 00174 return 0; // Entry actions differ, we assume ours is more recent. 00175 00176 _do_fib_modify(e, FIB_ENTRY_STATE_MOD_REMOVE); 00177 return 0; // Entry deactivated. 00178 } 00179 break; 00180 } 00181 00182 return -1; // Should never reach here. 00183 } 00184 00192 inline FibEntry<M, A, U>* get_entry (const M& m) 00193 { 00194 typename _fib_hash_type::iterator i = _fib_entries.find(&m); 00195 return i == _fib_entries.end() ? NULL : i->second; 00196 } 00197 00207 inline int update_hits (const M& m, const uint64_t& hits, const enum FibEntryHitsUpdateType type = FIB_ENTRY_HITS_ADD) 00208 { 00209 typename _fib_hash_type::iterator i = _fib_entries.find(&m); 00210 if (i == _fib_entries.end()) 00211 return -1; 00212 00213 i->second->update_hits(hits, type); 00214 00215 return 0; 00216 } 00217 00224 void bin_complete (void) 00225 { 00226 _fib_multiset_b1_type b1_set; 00227 _fib_multiset_b2_type b2_set; 00228 _fib_multiset_b3_type b3_set; 00229 00230 for (typename _fib_hash_type::iterator i = _fib_entries.begin(); 00231 i != _fib_entries.end(); 00232 ) 00233 { 00234 FibEntry<M, A, U>* e = i->second; 00235 00236 // Compute final b1, b2, b3 counters by calling bin_complete for all entries. 00237 e->bin_complete(); 00238 00239 if (e->state() == FIB_ENTRY_STATE_INACTIVE) 00240 { 00241 typename _fib_hash_type::iterator i_next = i; 00242 i_next++; 00243 00244 // This entry is inactive. Decrement TTL and remove if necessary. 00245 if (e->ttl_decrement() == 0) 00246 { 00247 _fib_entries.erase(i); 00248 delete e; 00249 } 00250 00251 i = i_next; 00252 continue; 00253 } 00254 00255 // Create sorted sets of entries by b1, b2, b3 counters 00256 b1_set.insert(e); 00257 b2_set.insert(e); 00258 b3_set.insert(e); 00259 00260 ++i; 00261 } 00262 00263 // Apply selection strategy. 00264 _fib_set_type top_n; 00265 typename _fib_multiset_b1_type::reverse_iterator i_b1 = b1_set.rbegin(); 00266 typename _fib_multiset_b2_type::reverse_iterator i_b2 = b2_set.rbegin(); 00267 typename _fib_multiset_b3_type::reverse_iterator i_b3 = b3_set.rbegin(); 00268 00269 // Round-robin select popular entries (1). 00270 for (uint32_t i = 0, all_in = 0; 00271 top_n.size() < _fib_capacity && all_in != 0x7; 00272 ++i) 00273 { 00274 switch (i % 3) 00275 { 00276 case 0: 00277 if (i_b1 != b1_set.rend()) 00278 top_n.insert(*i_b1++); 00279 else 00280 all_in |= 1 << 0; 00281 break; 00282 case 1: 00283 if (i_b2 != b2_set.rend()) 00284 top_n.insert(*i_b2++); 00285 else 00286 all_in |= 1 << 1; 00287 break; 00288 case 2: 00289 if (i_b3 != b3_set.rend()) 00290 top_n.insert(*i_b3++); 00291 else 00292 all_in |= 1 << 2; 00293 break; 00294 } 00295 } 00296 00297 // Compute difference (2), sorted by popularity (3). 00298 _fib_multiset_b1_type top_n_adds; 00299 _fib_multiset_b1_type top_n_rems; 00300 for (typename _fib_hash_type::iterator i = _fib_entries.begin(); 00301 i != _fib_entries.end(); 00302 ++i) 00303 { 00304 FibEntry<M, A, U>* e = i->second; 00305 if (e->state() == FIB_ENTRY_STATE_INACTIVE) 00306 continue; 00307 00308 if (top_n.find(e) != top_n.end()) 00309 { 00310 if (e->table() == FIB_ENTRY_TABLE_SLOW) 00311 { 00312 top_n_adds.insert(e); 00313 } 00314 } 00315 else 00316 { 00317 if (e->table() == FIB_ENTRY_TABLE_FAST) 00318 { 00319 top_n_rems.insert(e); 00320 } 00321 } 00322 } 00323 00324 // Select high-value FIB cache changes (4). 00325 list<FibEntry<M, A, U>* > top_n_mods; 00326 typename _fib_multiset_b1_type::reverse_iterator i_adds = top_n_adds.rbegin(); 00327 typename _fib_multiset_b1_type::iterator i_rems = top_n_rems.begin(); 00328 00329 // If there are more additions than removals, select those extra additions starting 00330 // at the most promising ones. 00331 for (int i = top_n_adds.size() - top_n_rems.size(); i > 0; --i) 00332 { 00333 FibEntry<M, A, U>* e = *i_adds++; 00334 e->table(FIB_ENTRY_TABLE_FAST); 00335 top_n_mods.push_front(e); 00336 } 00337 00338 // If there are more removals than additions, select those extra removals starting 00339 // at the least promising ones. 00340 for (int i = top_n_rems.size() - top_n_adds.size(); i > 0; --i) 00341 { 00342 FibEntry<M, A, U>* e = *i_rems++; 00343 e->table(FIB_ENTRY_TABLE_SLOW); 00344 top_n_mods.push_back(e); 00345 } 00346 00347 // Finally select all significant changes. 00348 while (i_adds != top_n_adds.rend() && 00349 i_rems != top_n_rems.end()) 00350 { 00351 FibEntry<M, A, U>* e_add = *i_adds++; 00352 FibEntry<M, A, U>* e_rem = *i_rems++; 00353 00354 if (e_add->hits_b1() > (e_rem->hits_b1() * 2)) 00355 { 00356 e_add->table(FIB_ENTRY_TABLE_FAST); 00357 top_n_mods.push_front(e_add); 00358 00359 e_rem->table(FIB_ENTRY_TABLE_SLOW); 00360 top_n_mods.push_back(e_rem); 00361 } 00362 else 00363 break; 00364 } 00365 00366 // Apply modifications: alternate between removing from fast path and adding to fast path. 00367 for (bool front = false; top_n_mods.size(); front = !front) 00368 { 00369 FibEntry<M, A, U>* e; 00370 00371 if (front) 00372 { e = top_n_mods.front(); top_n_mods.pop_front(); } 00373 else 00374 { e = top_n_mods.back(); top_n_mods.pop_back(); } 00375 00376 _do_fib_modify(e, FIB_ENTRY_STATE_MOD_MOVE); 00377 } 00378 } 00379 00386 void walk_entries (walk_const_callback_func f, void* user_data = NULL) 00387 { 00388 for (typename _fib_hash_type::const_iterator i = _fib_entries.begin(); 00389 i != _fib_entries.end(); 00390 ++i) 00391 { 00392 f(_owner, i->second, user_data); 00393 } 00394 } 00395 00402 void walk_entries (walk_callback_func f, void* user_data = NULL) 00403 { 00404 for (typename _fib_hash_type::iterator i = _fib_entries.begin(); 00405 i != _fib_entries.end(); 00406 ++i) 00407 { 00408 f(_owner, i->second, user_data); 00409 } 00410 } 00411 00412 private: 00413 O _owner; 00414 00415 uint32_t _fib_capacity; 00416 fib_modify_func _fib_modify; 00417 00418 uint32_t _size_b2; 00419 uint32_t _size_b3; 00420 00421 AEqualFcn _action_equal; 00422 00423 typedef hash_map<const M*, FibEntry<M, A, U>*, MHashFcn, MEqualFcn> _fib_hash_type; 00424 _fib_hash_type _fib_entries; 00425 00427 typedef set<FibEntry<M, A, U>*> _fib_set_type; 00428 00429 struct _fib_entry_b1_lt 00430 { 00431 bool operator() (FibEntry<M, A, U>* a, FibEntry<M, A, U>* b) 00432 { return a->hits_b1() < b->hits_b1(); } 00433 }; 00434 typedef multiset<FibEntry<M, A, U>*, _fib_entry_b1_lt> _fib_multiset_b1_type; 00435 00436 struct _fib_entry_b2_lt 00437 { 00438 bool operator() (FibEntry<M, A, U>* a, FibEntry<M, A, U>* b) 00439 { return a->hits_b2() < b->hits_b2(); } 00440 }; 00441 typedef multiset<FibEntry<M, A, U>*, _fib_entry_b2_lt> _fib_multiset_b2_type; 00442 00443 struct _fib_entry_b3_lt 00444 { 00445 bool operator() (FibEntry<M, A, U>* a, FibEntry<M, A, U>* b) 00446 { return a->hits_b3() < b->hits_b3(); } 00447 }; 00448 typedef multiset<FibEntry<M, A, U>*, _fib_entry_b3_lt> _fib_multiset_b3_type; 00451 inline void _do_fib_modify (FibEntry<M, A, U>* e, enum FibEntryState s) 00452 { 00453 e->state(s); 00454 _fib_modify(_owner, e); 00455 00456 switch (s) 00457 { 00458 case FIB_ENTRY_STATE_MOD_ADD: 00459 e->ttl(0); 00460 e->state(FIB_ENTRY_STATE_ACTIVE); 00461 break; 00462 case FIB_ENTRY_STATE_MOD_MOVE: 00463 e->state(FIB_ENTRY_STATE_ACTIVE); 00464 break; 00465 case FIB_ENTRY_STATE_MOD_ACTION: 00466 e->old_action(e->action()); 00467 e->state(FIB_ENTRY_STATE_ACTIVE); 00468 break; 00469 case FIB_ENTRY_STATE_MOD_REMOVE: 00470 e->ttl(_size_b3); 00471 e->state(FIB_ENTRY_STATE_INACTIVE); 00472 break; 00473 default: 00474 break; 00475 } 00476 } 00477 }; 00478 00479 #endif