libTFO
include/libtfo/tfo.hh
Go to the documentation of this file.
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
 All Classes Files Functions Typedefs Enumerations Enumerator