LCOV - code coverage report
Current view: top level - vnsw/agent/pkt - flow_table.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 321 510 62.9 %
Date: 2026-06-08 02:02:55 Functions: 35 47 74.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  */
       4             : 
       5             : #include <vector>
       6             : #include <bitset>
       7             : #include <mutex>
       8             : 
       9             : #include <boost/date_time/posix_time/posix_time.hpp>
      10             : #include <boost/assign/list_of.hpp>
      11             : #include <boost/unordered_map.hpp>
      12             : #include <sandesh/sandesh_trace.h>
      13             : #include <base/address_util.h>
      14             : #include <pkt/flow_table.h>
      15             : #include <vrouter/ksync/ksync_init.h>
      16             : #include <vrouter/ksync/ksync_flow_index_manager.h>
      17             : 
      18             : #include <arpa/inet.h>
      19             : #include <netinet/in.h>
      20             : #include <base/os.h>
      21             : 
      22             : #include <route/route.h>
      23             : #include <cmn/agent_cmn.h>
      24             : #include <oper/interface_common.h>
      25             : #include <oper/nexthop.h>
      26             : 
      27             : #include <init/agent_param.h>
      28             : #include <cmn/agent_cmn.h>
      29             : #include <cmn/agent_stats.h>
      30             : #include <oper/route_common.h>
      31             : #include <oper/vrf.h>
      32             : #include <oper/vm.h>
      33             : #include <oper/sg.h>
      34             : #include <oper/tunnel_nh.h>
      35             : 
      36             : #include <filter/packet_header.h>
      37             : #include <filter/acl.h>
      38             : 
      39             : #include <pkt/proto.h>
      40             : #include <pkt/proto_handler.h>
      41             : #include <pkt/pkt_handler.h>
      42             : #include <pkt/flow_proto.h>
      43             : #include <pkt/pkt_types.h>
      44             : #include <pkt/pkt_sandesh_flow.h>
      45             : #include <pkt/flow_mgmt.h>
      46             : #include <pkt/flow_event.h>
      47             : 
      48             : const uint32_t FlowEntryFreeList::kInitCount;
      49             : const uint32_t FlowEntryFreeList::kTestInitCount;
      50             : const uint32_t FlowEntryFreeList::kGrowSize;
      51             : const uint32_t FlowEntryFreeList::kMinThreshold;
      52             : const uint32_t FlowEntryFreeList::kMaxThreshold;
      53             : 
      54             : SandeshTraceBufferPtr FlowTraceBuf(SandeshTraceBufferCreate("Flow", 5000));
      55             : 
      56             : /////////////////////////////////////////////////////////////////////////////
      57             : // FlowTable constructor/destructor
      58             : /////////////////////////////////////////////////////////////////////////////
      59           1 : FlowTable::FlowTable(Agent *agent, uint16_t table_index) :
      60           1 :     agent_(agent),
      61             :     rand_gen_(boost::uuids::random_generator()),
      62           1 :     table_index_(table_index),
      63           1 :     ksync_object_(NULL),
      64           1 :     flow_entry_map_(),
      65           1 :     free_list_(this),
      66           1 :     flow_task_id_(0),
      67           1 :     flow_update_task_id_(0),
      68           1 :     flow_delete_task_id_(0),
      69           1 :     flow_ksync_task_id_(0),
      70           2 :     flow_logging_task_id_(0) {
      71           1 : }
      72             : 
      73           2 : FlowTable::~FlowTable() {
      74           1 :     assert(flow_entry_map_.size() == 0);
      75           2 : }
      76             : 
      77           1 : void FlowTable::Init() {
      78           1 :     flow_task_id_ = agent_->task_scheduler()->GetTaskId(kTaskFlowEvent);
      79           1 :     flow_update_task_id_ = agent_->task_scheduler()->GetTaskId(kTaskFlowUpdate);
      80           1 :     flow_delete_task_id_ = agent_->task_scheduler()->GetTaskId(kTaskFlowDelete);
      81           1 :     flow_ksync_task_id_ = agent_->task_scheduler()->GetTaskId(kTaskFlowKSync);
      82           1 :     flow_logging_task_id_ = agent_->task_scheduler()->GetTaskId(kTaskFlowLogging);
      83           1 :     FlowEntry::Init();
      84           1 :     return;
      85             : }
      86             : 
      87           1 : void FlowTable::InitDone() {
      88           1 : }
      89             : 
      90           1 : void FlowTable::Shutdown() {
      91           1 : }
      92             : 
      93             : // Concurrency check to ensure all flow-table and free-list manipulations
      94             : // are done from FlowEvent task context only
      95             : //exception: freelist free function can be accessed by flow logging task
      96         929 : bool FlowTable::ConcurrencyCheck(int task_id, bool check_task_instance) {
      97         929 :     Task *current = Task::Running();
      98             :     // test code invokes FlowTable API from main thread. The running task
      99             :     // will be NULL in such cases
     100         928 :     if (current == NULL) {
     101          42 :         return true;
     102             :     }
     103             : 
     104         886 :     if (current->task_code_id() != task_id)
     105          44 :         return false;
     106         842 :     if (check_task_instance) {
     107         843 :         if (current->task_data_id() != table_index_)
     108           0 :            return false;
     109             :     }
     110         842 :     return true;
     111             : }
     112             : 
     113         929 : bool FlowTable::ConcurrencyCheck(int task_id) {
     114         929 :         return ConcurrencyCheck(task_id, true);
     115             : }
     116             : 
     117             : /////////////////////////////////////////////////////////////////////////////
     118             : // FlowTable Add/Delete routines
     119             : /////////////////////////////////////////////////////////////////////////////
     120             : 
     121          92 : FlowEntry *FlowTable::Find(const FlowKey &key) {
     122          92 :     assert(ConcurrencyCheck(flow_task_id_) == true);
     123          92 :     FlowEntryMap::iterator it;
     124             : 
     125          92 :     it = flow_entry_map_.find(key);
     126          92 :     if (it != flow_entry_map_.end()) {
     127          92 :         return it->second;
     128             :     } else {
     129           0 :         return NULL;
     130             :     }
     131             : }
     132             : 
     133          50 : void FlowTable::Copy(FlowEntry *lhs, FlowEntry *rhs, bool update) {
     134             :     /* Flow copy, if results in UUID change, stop updating UVE stats
     135             :      * for old flow
     136             :      */
     137          50 :     if (update==false)
     138           0 :         DeleteFlowUveInfo(lhs);
     139             : 
     140          50 :     RevFlowDepParams params;
     141          50 :     lhs->RevFlowDepInfo(&params);
     142          50 :     DeleteFlowInfo(lhs, params);
     143          50 :     if (rhs)
     144          50 :         lhs->Copy(rhs, update);
     145          50 : }
     146             : 
     147          44 : FlowEntry *FlowTable::Locate(FlowEntry *flow, uint64_t time) {
     148          44 :     assert(ConcurrencyCheck(flow_task_id_) == true);
     149          44 :     std::pair<FlowEntryMap::iterator, bool> ret;
     150          44 :     ret = flow_entry_map_.insert(FlowEntryMapPair(flow->key(), flow));
     151          44 :     if (ret.second == true) {
     152          44 :         agent_->stats()->incr_flow_created();
     153          44 :         ret.first->second->set_on_tree();
     154          44 :         return flow;
     155             :     }
     156             : 
     157           0 :     return ret.first->second;
     158             : }
     159             : 
     160          22 : void FlowTable::Add(FlowEntry *flow, FlowEntry *rflow) {
     161          22 :     uint64_t time = UTCTimestampUsec();
     162          22 :     FlowEntry *new_flow = Locate(flow, time);
     163          22 :     FlowEntry *new_rflow = (rflow != NULL) ? Locate(rflow, time) : NULL;
     164             : 
     165          22 :     FLOW_LOCK(new_flow, new_rflow, FlowEvent::FLOW_MESSAGE);
     166          22 :     AddInternal(flow, new_flow, rflow, new_rflow, false, false);
     167          22 : }
     168             : 
     169          25 : void FlowTable::Update(FlowEntry *flow, FlowEntry *rflow) {
     170          25 :     bool fwd_flow_update = true;
     171          25 :     FlowEntry *new_flow = Find(flow->key());
     172             : 
     173          25 :     FlowEntry *new_rflow = (rflow != NULL) ? Find(rflow->key()) : NULL;
     174          25 :     bool rev_flow_update = true;
     175          25 :     if (rflow && new_rflow == NULL) {
     176           0 :         uint64_t time = UTCTimestampUsec();
     177           0 :         new_rflow = Locate(rflow, time);
     178           0 :         rev_flow_update = false;
     179             :     }
     180             : 
     181          25 :     FLOW_LOCK(new_flow, new_rflow, FlowEvent::FLOW_MESSAGE);
     182          25 :     AddInternal(flow, new_flow, rflow, new_rflow, fwd_flow_update,
     183             :                 rev_flow_update);
     184          25 : }
     185             : 
     186          47 : void FlowTable::AddInternal(FlowEntry *flow_req, FlowEntry *flow,
     187             :                             FlowEntry *rflow_req, FlowEntry *rflow,
     188             :                             bool fwd_flow_update, bool rev_flow_update) {
     189             :     // Set trace flags for a flow
     190          47 :     bool trace = agent_->pkt()->get_flow_proto()->ShouldTrace(flow, rflow);
     191          47 :     if (flow)
     192          47 :         flow->set_trace(trace);
     193          47 :     if (rflow)
     194          47 :         rflow->set_trace(trace);
     195             : 
     196             :     // The forward and reverse flow in request are linked. Unlink the flows
     197             :     // first. Flow table processing will link them if necessary
     198          47 :     flow_req->set_reverse_flow_entry(NULL);
     199          47 :     if (rflow_req)
     200          47 :         rflow_req->set_reverse_flow_entry(NULL);
     201             : 
     202          47 :     bool force_update_rflow = false;
     203          47 :     if (fwd_flow_update) {
     204          25 :         if (flow == NULL)
     205           0 :             return;
     206             : 
     207          25 :         if (flow->deleted() || flow->IsShortFlow()) {
     208           0 :             return;
     209             :         }
     210             :     }
     211             : 
     212          47 :     if (flow_req != flow) {
     213          25 :         if (flow->flow_handle() == FlowEntry::kInvalidFlowHandle &&
     214           0 :             !flow->deleted()) {
     215             :             // In this scenario packet trap for forward flow should
     216             :             // not cause eviction of the reverse flow due to add event
     217             :             // so trigger a force update instead of add for reverse flow
     218           0 :             force_update_rflow = true;
     219             :         }
     220          25 :         Copy(flow, flow_req, fwd_flow_update);
     221          25 :         flow->set_deleted(false);
     222             :         // this flow entry is reused , increment the transaction id
     223             :         // so that flow events with old transaction id will be ingnored
     224          25 :         flow->IncrementTransactionId();
     225             :     }
     226             : 
     227          47 :     if (rflow) {
     228          47 :         if (rflow_req != rflow) {
     229          25 :             Copy(rflow, rflow_req, (rev_flow_update || force_update_rflow));
     230             :             // if the reverse flow was marked delete, reset its flow handle
     231             :             // to invalid index to assure it is attempted to reprogram using
     232             :             // kInvalidFlowHandle, this also ensures that flow entry wont
     233             :             // give fake notion of being available in the flow index tree
     234             :             // delete for which has already happend while triggering delete
     235             :             // for flow entry
     236          25 :             if (rflow->deleted()) {
     237           0 :                 rflow->flow_handle_ = FlowEntry::kInvalidFlowHandle;
     238             :                 // rflow was delete marked skip force update
     239           0 :                 force_update_rflow = false;
     240             :             }
     241          25 :             rflow->set_deleted(false);
     242             :             // this flow entry is reused , increment the transaction id
     243             :             // so that flow events with old transaction id will be ingnored
     244          25 :             rflow->IncrementTransactionId();
     245             :         } else {
     246             :             // we are creating a new reverse flow, so avoid triggering
     247             :             // force update in this case
     248          22 :             force_update_rflow = false;
     249             :         }
     250             :     }
     251             : 
     252          47 :     if (flow) {
     253          47 :         if (fwd_flow_update) {
     254          25 :             flow->set_last_event(FlowEvent::FLOW_MESSAGE);
     255             :         } else {
     256          22 :             flow->set_last_event(FlowEvent::VROUTER_FLOW_MSG);
     257             :         }
     258             :     }
     259          47 :     if (rflow) {
     260          47 :         if (rev_flow_update) {
     261          25 :             rflow->set_last_event(FlowEvent::FLOW_MESSAGE);
     262             :         } else {
     263          22 :             rflow->set_last_event(FlowEvent::VROUTER_FLOW_MSG);
     264             :         }
     265             :     }
     266             : 
     267             :     // If the flows are already present, we want to retain the Forward and
     268             :     // Reverse flow characteristics for flow.
     269             :     // We have following conditions,
     270             :     // flow has ReverseFlow set, rflow has ReverseFlow reset
     271             :     //      Swap flow and rflow
     272             :     // flow has ReverseFlow set, rflow has ReverseFlow set
     273             :     //      Unexpected case. Continue with flow as forward flow
     274             :     // flow has ReverseFlow reset, rflow has ReverseFlow reset
     275             :     //      Unexpected case. Continue with flow as forward flow
     276             :     // flow has ReverseFlow reset, rflow has ReverseFlow set
     277             :     //      No change in forward/reverse flow. Continue as forward-flow
     278          47 :     if (flow->is_flags_set(FlowEntry::ReverseFlow) &&
     279          47 :         rflow && !rflow->is_flags_set(FlowEntry::ReverseFlow)) {
     280           0 :         FlowEntry *tmp = flow;
     281           0 :         flow = rflow;
     282           0 :         rflow = tmp;
     283             :     }
     284             : 
     285          47 :     UpdateReverseFlow(flow, rflow);
     286             : 
     287             :     // Add the forward flow after adding the reverse flow first to avoid
     288             :     // following sequence
     289             :     // 1. Agent adds forward flow
     290             :     // 2. vrouter releases the packet
     291             :     // 3. Packet reaches destination VM and destination VM replies
     292             :     // 4. Agent tries adding reverse flow. vrouter processes request in core-0
     293             :     // 5. vrouter gets reverse packet in core-1
     294             :     // 6. If (4) and (3) happen together, vrouter can allocate 2 hash entries
     295             :     //    for the flow.
     296             :     //
     297             :     // While the scenario above cannot be totally avoided, programming reverse
     298             :     // flow first will reduce the probability
     299          47 :     if (rflow) {
     300          47 :         UpdateKSync(rflow, (rev_flow_update || force_update_rflow));
     301          47 :         AddFlowInfo(rflow);
     302             :     }
     303             : 
     304          47 :     UpdateKSync(flow, fwd_flow_update);
     305          47 :     AddFlowInfo(flow);
     306             : }
     307             : 
     308          44 : void FlowTable::DeleteInternal(FlowEntry *fe, uint64_t time,
     309             :                                const RevFlowDepParams &params) {
     310          44 :     fe->set_deleted(true);
     311             : 
     312             :     // Unlink the reverse flow, if one exists
     313          44 :     FlowEntry *rflow = fe->reverse_flow_entry();
     314          44 :     if (rflow) {
     315          22 :         rflow->set_reverse_flow_entry(NULL);
     316             :     }
     317          44 :     fe->set_reverse_flow_entry(NULL);
     318             : 
     319          44 :     ReleasePort(fe, true);
     320             : 
     321          44 :     DeleteFlowInfo(fe, params);
     322          44 :     DeleteKSync(fe);
     323             : 
     324          44 :     agent_->stats()->incr_flow_aged();
     325          44 : }
     326             : 
     327          37 : bool FlowTable::DeleteFlows(FlowEntry *flow, FlowEntry *rflow) {
     328          37 :     uint64_t time = UTCTimestampUsec();
     329             : 
     330             :     /* Fetch reverse-flow info for both flows before their reverse-flow
     331             :      * links are broken. This info is required during FlowExport
     332             :      *
     333             :      * DeleteFlows() is invoked for both forward and reverse flows. So, get
     334             :      * reverse-flow info only when flows are not deleted
     335             :      */
     336          37 :     RevFlowDepParams r_params;
     337          37 :     if (rflow && rflow->deleted() == false) {
     338          22 :         rflow->RevFlowDepInfo(&r_params);
     339             :     }
     340          37 :     if (flow && flow->deleted() == false) {
     341          22 :         RevFlowDepParams f_params;
     342          22 :         flow->RevFlowDepInfo(&f_params);
     343             :         /* Delete the forward flow */
     344          22 :         DeleteInternal(flow, time, f_params);
     345          22 :     }
     346             : 
     347          37 :     if (rflow && rflow->deleted() == false) {
     348          22 :         DeleteInternal(rflow, time, r_params);
     349             :     }
     350          37 :     return true;
     351          37 : }
     352             : 
     353           0 : void FlowTable::PopulateFlowEntriesUsingKey(const FlowKey &key,
     354             :                                             bool reverse_flow,
     355             :                                             FlowEntry** flow,
     356             :                                             FlowEntry** rflow) {
     357           0 :     *flow = Find(key);
     358           0 :     *rflow = NULL;
     359             : 
     360             :     //No flow entry, nothing to populate
     361           0 :     if (!(*flow)) {
     362           0 :         return;
     363             :     }
     364             : 
     365             :     //No reverse flow requested, so dont populate rflow
     366           0 :     if (!reverse_flow) {
     367           0 :         return;
     368             :     }
     369             : 
     370           0 :     FlowEntry *reverse_flow_entry = (*flow)->reverse_flow_entry();
     371           0 :     if (reverse_flow_entry) {
     372           0 :         *rflow = Find(reverse_flow_entry->key());
     373             :     }
     374             : }
     375             : 
     376             : //Caller makes sure lock is taken on flow.
     377          37 : bool FlowTable::DeleteUnLocked(bool del_reverse_flow,
     378             :                                FlowEntry *flow,
     379             :                                FlowEntry *rflow) {
     380          37 :     if (!flow) {
     381           0 :         return false;
     382             :     }
     383             : 
     384          37 :     DeleteFlows(flow, rflow);
     385             : 
     386             :     //If deletion of reverse flow is to be done,
     387             :     //make sure that rflow is populated if flow has a reverse flow pointer.
     388             :     //In case rflow is not located with the reverse flow key, consider it as
     389             :     //failure.
     390          37 :     if (del_reverse_flow && flow->reverse_flow_entry() && !rflow) {
     391           0 :         return false;
     392             :     }
     393          37 :     return true;
     394             : }
     395             : 
     396             : //Caller has to ensure lock is taken for flow.
     397           0 : bool FlowTable::DeleteUnLocked(const FlowKey &key, bool del_reverse_flow) {
     398           0 :     FlowEntry *flow = NULL;
     399           0 :     FlowEntry *rflow = NULL;
     400             : 
     401           0 :     PopulateFlowEntriesUsingKey(key, del_reverse_flow, &flow, &rflow);
     402           0 :     return DeleteUnLocked(del_reverse_flow, flow, rflow);
     403             : }
     404             : 
     405           0 : bool FlowTable::Delete(const FlowKey &key, bool del_reverse_flow) {
     406           0 :     FlowEntry *flow = NULL;
     407           0 :     FlowEntry *rflow = NULL;
     408             : 
     409           0 :     PopulateFlowEntriesUsingKey(key, del_reverse_flow, &flow, &rflow);
     410           0 :     FLOW_LOCK(flow, rflow, FlowEvent::DELETE_FLOW);
     411           0 :     return DeleteUnLocked(del_reverse_flow, flow, rflow);
     412           0 : }
     413             : 
     414           1 : void FlowTable::DeleteAll() {
     415           1 :     FlowEntryMap::iterator it;
     416             : 
     417           1 :     it = flow_entry_map_.begin();
     418           1 :     while (it != flow_entry_map_.end()) {
     419           0 :         FlowEntry *entry = it->second;
     420           0 :         FlowEntry *reverse_entry = NULL;
     421           0 :         ++it;
     422           0 :         if (it != flow_entry_map_.end() &&
     423           0 :             it->second == entry->reverse_flow_entry()) {
     424           0 :             reverse_entry = it->second;
     425           0 :             ++it;
     426             :         }
     427           0 :         FLOW_LOCK(entry, reverse_entry, FlowEvent::DELETE_FLOW);
     428           0 :         DeleteUnLocked(true, entry, reverse_entry);
     429           0 :     }
     430           1 : }
     431             : 
     432          47 : void FlowTable::UpdateReverseFlow(FlowEntry *flow, FlowEntry *rflow) {
     433          47 :     FlowEntry *flow_rev = flow->reverse_flow_entry();
     434          47 :     FlowEntry *rflow_rev = NULL;
     435             : 
     436          47 :     if (rflow) {
     437          47 :         rflow_rev = rflow->reverse_flow_entry();
     438             :     }
     439             : 
     440          47 :     if (rflow_rev) {
     441          25 :         assert(rflow_rev->reverse_flow_entry() == rflow);
     442          25 :         rflow_rev->set_reverse_flow_entry(NULL);
     443             :     }
     444             : 
     445          47 :     if (flow_rev) {
     446          25 :         flow_rev->set_reverse_flow_entry(NULL);
     447             :     }
     448             : 
     449          47 :     flow->set_reverse_flow_entry(rflow);
     450          47 :     if (rflow) {
     451          47 :         rflow->set_reverse_flow_entry(flow);
     452             :     }
     453             : 
     454          47 :     if (flow_rev && (flow_rev->reverse_flow_entry() == NULL)) {
     455           0 :         flow_rev->MakeShortFlow(FlowEntry::SHORT_NO_REVERSE_FLOW);
     456             :     }
     457             : 
     458          47 :     if (rflow_rev && (rflow_rev->reverse_flow_entry() == NULL)) {
     459           0 :         rflow_rev->MakeShortFlow(FlowEntry::SHORT_NO_REVERSE_FLOW);
     460             :     }
     461             : 
     462          47 :     if (flow->reverse_flow_entry() == NULL) {
     463           0 :         flow->MakeShortFlow(FlowEntry::SHORT_NO_REVERSE_FLOW);
     464             :     }
     465             : 
     466          47 :     if (rflow && rflow->reverse_flow_entry() == NULL) {
     467           0 :         rflow->MakeShortFlow(FlowEntry::SHORT_NO_REVERSE_FLOW);
     468             :     }
     469             : 
     470          47 :     if (rflow) {
     471          83 :         if (flow->is_flags_set(FlowEntry::ShortFlow) ||
     472          83 :             rflow->is_flags_set(FlowEntry::ShortFlow)) {
     473          11 :             flow->MakeShortFlow(FlowEntry::SHORT_REVERSE_FLOW_CHANGE);
     474             :         }
     475          47 :         if (flow->is_flags_set(FlowEntry::Multicast)) {
     476           0 :             rflow->set_flags(FlowEntry::Multicast);
     477             :         }
     478             :     }
     479          47 : }
     480             : 
     481           0 : void FlowTable::DeleteFlowUveInfo(FlowEntry *fe) {
     482           0 :     agent_->pkt()->flow_mgmt_manager(table_index_)->EnqueueUveDeleteEvent(fe);
     483           0 : }
     484             : 
     485             : ////////////////////////////////////////////////////////////////////////////
     486             : // Flow Info tree management
     487             : ////////////////////////////////////////////////////////////////////////////
     488         100 : void FlowTable::AddFlowInfo(FlowEntry *fe) {
     489         100 :     agent_->pkt()->flow_mgmt_manager(table_index_)->AddEvent(fe);
     490         100 : }
     491             : 
     492          94 : void FlowTable::DeleteFlowInfo(FlowEntry *fe, const RevFlowDepParams &params) {
     493          94 :     agent_->pkt()->flow_mgmt_manager(table_index_)->DeleteEvent(fe, params);
     494          94 : }
     495             : 
     496             : /////////////////////////////////////////////////////////////////////////////
     497             : // Flow revluation routines. Processing will vary based on DBEntry type
     498             : /////////////////////////////////////////////////////////////////////////////
     499       20376 : boost::uuids::uuid FlowTable::rand_gen() {
     500       20376 :     return rand_gen_();
     501             : }
     502             : 
     503             : // Enqueue message to recompute a flow
     504          27 : void FlowTable::RecomputeFlow(FlowEntry *flow) {
     505          27 :     if (flow->is_flags_set(FlowEntry::ShortFlow))
     506           0 :         return;
     507             : 
     508             :     // If this is reverse flow, enqueue the corresponding forward flow
     509          27 :     if (flow->is_flags_set(FlowEntry::ReverseFlow)) {
     510          11 :         flow = flow->reverse_flow_entry();
     511             :     }
     512             : 
     513          27 :     if (flow != NULL)
     514          27 :         agent_->pkt()->get_flow_proto()->MessageRequest(flow);
     515             : }
     516             : 
     517             : // Handle deletion of a Route. Flow management module has identified that route
     518             : // must be deleted
     519          37 : void FlowTable::DeleteMessage(FlowEntry *flow) {
     520          37 :     DeleteUnLocked(true, flow, flow->reverse_flow_entry());
     521          37 : }
     522             : 
     523          44 : void FlowTable::ReleasePort(FlowEntry *flow, bool evict) {
     524          44 :     if (flow->allocated_port() == 0) {
     525          44 :         return;
     526             :     }
     527             : 
     528           0 :     PortTableManager *pm = agent_->pkt()->get_flow_proto()->port_table_manager();
     529           0 :     pm->Free(flow->key(), flow->allocated_port(), evict);
     530             : }
     531             : 
     532           0 : void FlowTable::EvictFlow(FlowEntry *flow, FlowEntry *reverse_flow,
     533             :                           uint32_t evict_gen_id) {
     534           0 :     DisableKSyncSend(flow, evict_gen_id);
     535           0 :     DeleteUnLocked(false, flow, NULL);
     536             : 
     537             :     // Reverse flow unlinked with forward flow. Make it short-flow
     538             :     // Dont update ksync, it will shortly get either evicted or deleted by
     539             :     // ageing process
     540           0 :     if (reverse_flow)
     541           0 :         reverse_flow->MakeShortFlow(FlowEntry::SHORT_NO_REVERSE_FLOW);
     542           0 : }
     543             : 
     544           3 : void FlowTable::HandleRevaluateDBEntry(const DBEntry *entry, FlowEntry *flow,
     545             :                                        bool active_flow, bool deleted_flow) {
     546             :     // Ignore revluate of deleted/short flows
     547           3 :     if (flow->IsShortFlow())
     548           0 :         return;
     549             : 
     550           3 :     if (flow->deleted())
     551           0 :         return;
     552             : 
     553           3 :     FlowEntry *rflow = flow->reverse_flow_entry();
     554             :     // Update may happen for reverse-flow. We act on both forward and
     555             :     // reverse-flow. Get both forward and reverse flows
     556           3 :     if (flow->is_flags_set(FlowEntry::ReverseFlow)) {
     557           0 :         FlowEntry *tmp = flow;
     558           0 :         flow = rflow;
     559           0 :         rflow = tmp;
     560             :     }
     561             : 
     562             :     // We want to update only if both forward and reverse flow are valid
     563           3 :     if (flow == NULL || rflow == NULL)
     564           0 :         return;
     565             : 
     566             :     // Ignore update, if any of the DBEntries referred is deleted
     567           3 :     if (flow->vn_entry() && flow->vn_entry()->IsDeleted())
     568           0 :         return;
     569             : 
     570           3 :     if (flow->rpf_nh() && flow->rpf_nh()->IsDeleted())
     571           0 :         return;
     572             : 
     573           3 :     if (flow->intf_entry() && flow->intf_entry()->IsDeleted())
     574           0 :         return;
     575             : 
     576             :     // Revaluate flood unknown-unicast flag. If flow has UnknownUnicastFlood and
     577             :     // VN doesnt allow it, make Short Flow
     578           3 :     if (flow->vn_entry() &&
     579           6 :         flow->vn_entry()->flood_unknown_unicast() == false &&
     580           6 :         flow->is_flags_set(FlowEntry::UnknownUnicastFlood)) {
     581           0 :         flow->MakeShortFlow(FlowEntry::SHORT_NO_DST_ROUTE);
     582             :     }
     583             : 
     584             :     // On a l3mh compute, when physical interfaces state is down on host
     585             :     // make all flows with underlay_gw_index same as phy intf encap index short
     586           3 :     if (flow->data().underlay_gw_index_ != 0xff && agent()->is_l3mh()) {
     587           0 :         InetUnicastRouteEntry *rt = NULL;
     588           0 :         if (flow->is_flags_set(FlowEntry::IngressDir)) {
     589             :             rt = static_cast<InetUnicastRouteEntry *>
     590           0 :             (FlowEntry::GetUcRoute(flow->GetDestinationVrf(), flow->key().dst_addr));
     591           0 :         } else if (rflow->is_flags_set(FlowEntry::IngressDir)) {
     592             :             rt = static_cast<InetUnicastRouteEntry *>
     593           0 :             (FlowEntry::GetUcRoute(rflow->GetDestinationVrf(), rflow->key().dst_addr));
     594             :         }
     595           0 :         if (rt != NULL) {
     596             :             const TunnelNH *tunnel_nh =
     597           0 :                 dynamic_cast<const TunnelNH *>(rt->GetActiveNextHop());
     598           0 :             if (tunnel_nh != NULL) {
     599           0 :                 TunnelNH::EncapDataList encap_list = tunnel_nh->GetEncapDataList();
     600           0 :                 if (encap_list[flow->data().underlay_gw_index_].get()->valid_ == false) {
     601           0 :                     flow->MakeShortFlow(FlowEntry::SHORT_L3MH_PHY_INTF_DOWN);
     602             :                 }
     603           0 :             }
     604             :         }
     605             :     }
     606           3 :     flow->UpdateL2RouteInfo();
     607           3 :     rflow->UpdateL2RouteInfo();
     608             : 
     609             :     // Get policy attributes again and redo the flows
     610           3 :     flow->GetPolicyInfo();
     611           3 :     rflow->GetPolicyInfo();
     612             : 
     613             :     // Resync reverse flow first and then forward flow
     614             :     // as forward flow resync will try to update reverse flow
     615           3 :     rflow->ResyncFlow();
     616           3 :     flow->ResyncFlow();
     617             : 
     618             :     // RPF computation can be done only after policy processing.
     619             :     // Do RPF computation now
     620           3 :     flow->RpfUpdate();
     621           3 :     rflow->RpfUpdate();
     622             : 
     623             :     // the SG action could potentially have changed
     624             :     // due to reflexive nature. Update KSync for reverse flow first
     625           3 :     UpdateKSync(rflow, true);
     626           3 :     UpdateKSync(flow, true);
     627             : 
     628             :     // Update flow-mgmt with new values
     629           3 :     AddFlowInfo(flow);
     630           3 :     AddFlowInfo(rflow);
     631           3 :     return;
     632             : }
     633             : 
     634           0 : void FlowTable::HandleKSyncError(FlowEntry *flow,
     635             :                                  FlowTableKSyncEntry *ksync_entry,
     636             :                                  int ksync_error, uint32_t flow_handle,
     637             :                                  uint32_t gen_id) {
     638             :     // flow not associated with ksync anymore. Ignore the message
     639           0 :     if (flow == NULL || flow != ksync_entry->flow_entry()) {
     640           0 :         return;
     641             :     }
     642             : 
     643             :     // VRouter can return EBADF and ENONENT error if flow-handle changed before
     644             :     // getting KSync response back. Avoid making short-flow in such case
     645           0 :     if ((ksync_error == EBADF || ksync_error == ENOENT)) {
     646           0 :         if (flow->flow_handle() != flow_handle || flow->gen_id() != gen_id) {
     647           0 :             return;
     648             :         }
     649             :     }
     650             : 
     651             :     // If VRouter returns error, mark the flow entry as short flow and
     652             :     // update ksync error event to ksync index manager
     653             :     //
     654             :     // For EEXIST error donot mark the flow as ShortFlow since Vrouter
     655             :     // generates EEXIST only for cases where another add should be
     656             :     // coming from the pkt trap from Vrouter
     657           0 :     if (ksync_error != EEXIST || flow->is_flags_set(FlowEntry::NatFlow)) {
     658             :         // FIXME : We dont have good scheme to handle following scenario,
     659             :         // - VM1 in VN1 has floating-ip FIP1 in VN2
     660             :         // - VM2 in VN2
     661             :         // - VM1 pings VM2 (using floating-ip)
     662             :         // The forward and reverse flows go to different partitions.
     663             :         //
     664             :         // If packets for both forward and reverse flows are trapped together
     665             :         // we try to setup following flows from different partitions,
     666             :         // FlowPair-1
     667             :         //    - VM1 to VM2
     668             :         //    - VM2 to FIP1
     669             :         // FlowPair-2
     670             :         //    - VM2 to FIP1
     671             :         //    - VM1 to VM2
     672             :         //
     673             :         // CEM-18166: BGPaaS corrupted NAT flow after repeated BGP session flap
     674             :         // In the test environment it was observed that flows would remain stuck
     675             :         // with old reverse indexes, after a few operations of disabling and
     676             :         // re-enabling the interface on the VNF.
     677             :         // A previous attempt to fix this in CEM-10354 proved to be incomplete.
     678             :         // A better way to handle this case is to make the flows in question
     679             :         // short, but since EEXIST is returned for the RF (from Controller), it
     680             :         // is necessary to update the ksync_entry to the flow handle returned by
     681             :         // vrouter DP, Agent does not know it and is thus -1.
     682           0 :         if (flow->is_flags_set(FlowEntry::ReverseFlow) && 
     683           0 :             ksync_error == EEXIST && 
     684           0 :             flow->is_flags_set(FlowEntry::BgpRouterService)) {
     685             :             KSyncFlowIndexManager *mgr =
     686           0 :                     agent()->ksync()->ksync_flow_index_manager();
     687           0 :             mgr->UpdateFlowHandle(ksync_entry,
     688           0 :                                   ksync_entry->ksync_response_info()->flow_handle_,
     689           0 :                                   ksync_entry->ksync_response_info()->gen_id_);
     690           0 :             flow->MakeShortFlow(FlowEntry::SHORT_FAILED_VROUTER_INSTALL);
     691           0 :             return;
     692             :         }
     693             :         // The reverse flows for both FlowPair-1 and FlowPair-2 are not
     694             :         // installed due to EEXIST error. We are converting flows to
     695             :         // short-flow till this case is handled properly
     696           0 :         flow->MakeShortFlow(FlowEntry::SHORT_FAILED_VROUTER_INSTALL);
     697             :     }
     698           0 :     if (ksync_error == EEXIST) {
     699             :         const VmInterface *intf =
     700           0 :         dynamic_cast<const VmInterface*>(flow->data().intf_entry.get());
     701           0 :         KSyncFlowMemory *ksync_obj = agent()->ksync()->ksync_flow_memory();
     702           0 :         const vr_flow_entry *kflow = ksync_obj->GetKernelFlowEntry(flow_handle, false);
     703           0 :         if (kflow != nullptr &&
     704           0 :             intf != nullptr &&
     705           0 :             (kflow->fe_action != VR_FLOW_ACTION_HOLD)) {
     706           0 :             if (flow->flow_handle() == FlowEntry::kInvalidFlowHandle) {
     707             :                 KSyncFlowIndexManager *mgr =
     708           0 :                    agent()->ksync()->ksync_flow_index_manager();
     709           0 :                 mgr->UpdateFlowHandle(ksync_entry,
     710             :                                       flow_handle, //ksync_entry->ksync_response_info()->flow_handle_,
     711             :                                       gen_id); //ksync_entry->ksync_response_info()->gen_id_);
     712             :                 }
     713           0 :                 flow->MakeShortFlow(FlowEntry::SHORT_FAILED_VROUTER_INSTALL);
     714             :         }
     715             :     }
     716           0 :     return;
     717             : }
     718             : 
     719             : /////////////////////////////////////////////////////////////////////////////
     720             : // KSync Routines
     721             : /////////////////////////////////////////////////////////////////////////////
     722          44 : void FlowTable::DeleteKSync(FlowEntry *flow) {
     723          44 :     KSyncFlowIndexManager *mgr = agent()->ksync()->ksync_flow_index_manager();
     724          44 :     mgr->Delete(flow);
     725          44 : }
     726             : 
     727         122 : void FlowTable::UpdateKSync(FlowEntry *flow, bool update) {
     728         122 :     KSyncFlowIndexManager *mgr = agent()->ksync()->ksync_flow_index_manager();
     729         122 :     if (flow->deleted()) {
     730             :         // ignore update on a deleted flow
     731             :         // flow should already be non deleted of an Add case
     732           0 :         assert(update == false);
     733           0 :         return;
     734             :     }
     735         122 :     mgr->Update(flow);
     736             : }
     737             : 
     738           0 : void FlowTable::DisableKSyncSend(FlowEntry *flow, uint32_t evict_gen_id) {
     739           0 :     KSyncFlowIndexManager *mgr = agent()->ksync()->ksync_flow_index_manager();
     740           0 :     mgr->DisableSend(flow, evict_gen_id);
     741           0 : }
     742             : 
     743             : /////////////////////////////////////////////////////////////////////////////
     744             : // Link local flow information tree
     745             : /////////////////////////////////////////////////////////////////////////////
     746           0 : void FlowTable::AddLinkLocalFlowInfo(int fd, uint32_t index, const FlowKey &key,
     747             :                                      const uint64_t timestamp) {
     748           0 :     std::scoped_lock mutext(mutex_);
     749           0 :     LinkLocalFlowInfoMap::iterator it = linklocal_flow_info_map_.find(fd);
     750           0 :     if (it == linklocal_flow_info_map_.end()) {
     751           0 :         linklocal_flow_info_map_.insert(
     752           0 :           LinkLocalFlowInfoPair(fd, LinkLocalFlowInfo(index, key, timestamp)));
     753             :     } else {
     754           0 :         it->second.flow_index = index;
     755           0 :         it->second.flow_key = key;
     756             :     }
     757           0 : }
     758             : 
     759           0 : void FlowTable::DelLinkLocalFlowInfo(int fd) {
     760           0 :     std::scoped_lock mutext(mutex_);
     761           0 :     linklocal_flow_info_map_.erase(fd);
     762           0 : }
     763             : 
     764             : /////////////////////////////////////////////////////////////////////////////
     765             : // Event handler routines
     766             : /////////////////////////////////////////////////////////////////////////////
     767             : 
     768             : // KSync flow event handler. Handles response for both vr_flow message only
     769         144 : void FlowTable::ProcessKSyncFlowEvent(const FlowEventKSync *req,
     770             :                                       FlowEntry *flow) {
     771             :     FlowTableKSyncEntry *ksync_entry =
     772         144 :         (static_cast<FlowTableKSyncEntry *> (req->ksync_entry()));
     773         144 :     KSyncFlowIndexManager *imgr = agent()->ksync()->ksync_flow_index_manager();
     774             : 
     775             :     // flow not associated with ksync anymore. Ignore the message
     776         144 :     if (flow == NULL) {
     777           0 :         return;
     778             :     }
     779             : 
     780             :     // Ignore error for Delete messages
     781         144 :     if (req->ksync_event() == KSyncEntry::DEL_ACK) {
     782          44 :         return;
     783             :     }
     784             :     
     785             :     // if transaction id is not same, then ignore the old
     786             :     // vrouter add-ack response. this is possible that
     787             :     // after vrouter add-ack response, flows will be evicted
     788             :     // and new wflow with same flow tuple as evicted one will
     789             :     // trigger new flow request to agent. this old add-ack response
     790             :     // has no relevance, so should be ignored.
     791         100 :     if (req->transaction_id() != flow->GetTransactionId()) {
     792           0 :         return;
     793             :     }
     794             : 
     795         100 :     if (req->ksync_error() != 0) {
     796             :         // Handle KSync Errors
     797           0 :         HandleKSyncError(flow, ksync_entry, req->ksync_error(),
     798             :                          req->flow_handle(), req->gen_id());
     799             :     } else {
     800             :         // Operation succeeded. Update flow-handle if not assigned
     801             :         KSyncFlowIndexManager *mgr =
     802         100 :             agent()->ksync()->ksync_flow_index_manager();
     803         100 :         mgr->UpdateFlowHandle(ksync_entry, req->flow_handle(),
     804         100 :                               req->gen_id());
     805             :     }
     806             : 
     807             :     // Log message if flow-handle change
     808         100 :     if (flow->flow_handle() != FlowEntry::kInvalidFlowHandle) {
     809         100 :         if (flow->flow_handle() != req->flow_handle()) {
     810           0 :             LOG(DEBUG, "Flow index changed from <"
     811             :                 << flow->flow_handle() << "> to <"
     812             :                 << req->flow_handle() << ">");
     813             :         }
     814             :     }
     815             : 
     816             :     // When vrouter allocates a flow-index or changes flow-handle, its
     817             :     // possible that a flow in vrouter is evicted. Update stats for
     818             :     // evicted flow
     819         200 :     if (req->flow_handle() != FlowEntry::kInvalidFlowHandle &&
     820         100 :         req->flow_handle() != flow->flow_handle()) {
     821           0 :         FlowEntryPtr evicted_flow = imgr->FindByIndex(req->flow_handle());
     822           0 :         if (evicted_flow.get() && evicted_flow->deleted() == false) {
     823           0 :             FlowMgmtManager *mgr = agent()->pkt()->flow_mgmt_manager(table_index_);
     824           0 :             mgr->FlowStatsUpdateEvent(evicted_flow.get(),
     825           0 :                                       req->evict_flow_bytes(),
     826           0 :                                       req->evict_flow_packets(),
     827           0 :                                       req->evict_flow_oflow(),
     828             :                                       evicted_flow->uuid());
     829             :         }
     830           0 :     }
     831             : 
     832         100 :     return;
     833             : }
     834             : 
     835         217 : bool FlowTable::ProcessFlowEvent(const FlowEvent *req, FlowEntry *flow,
     836             :                                  FlowEntry *rflow) {
     837             :     //Take lock
     838         276 :     FLOW_LOCK(flow, rflow, req->event());
     839             : 
     840         217 :     if (flow)
     841         217 :         flow->set_last_event(req->event());
     842         217 :     if (rflow)
     843         158 :         rflow->set_last_event(req->event());
     844         217 :     bool active_flow = true;
     845         217 :     bool deleted_flow = flow->deleted();
     846         217 :     if (deleted_flow || flow->is_flags_set(FlowEntry::ShortFlow))
     847          98 :         active_flow = false;
     848             : 
     849             :     //Now process events.
     850         217 :     switch (req->event()) {
     851           0 :     case FlowEvent::DELETE_FLOW: {
     852             :         //In case of continous stream of short lived TCP flows with same 5 tuple,
     853             :         //flow eviction logic might cause below set of event
     854             :         //1> F1 and R1 flow are added to flow table
     855             :         //2> R1 is written to vrouter
     856             :         //3> F1 is written to vrouter
     857             :         //4> R1 flow add response is received, triggering update of
     858             :         //   F1(not needed now as reverse flow index is not written to kernel?)
     859             :         //5> In the meantime flow is evicted in vrouter, hence flow update for F1
     860             :         //   would result in error from vrouter resulting in short flow
     861             :         //6> Since F1 is shortflow Flow delete gets enqueued
     862             :         //7> Since R1 is evict marked, flow evict gets enqueued
     863             :         //8> Both event F1 and R1 delete and evict event can run in parallel,
     864             :         //   and hence reverse flow pointer obtained before FLOW lock could
     865             :         //   be invalid, hence read back the same
     866           0 :         rflow = flow->reverse_flow_entry();
     867           0 :         DeleteUnLocked(true, flow, rflow);
     868           0 :         break;
     869             :     }
     870             : 
     871          37 :     case FlowEvent::DELETE_DBENTRY: {
     872          37 :         DeleteMessage(flow);
     873          37 :         break;
     874             :     }
     875             : 
     876           3 :     case FlowEvent::REVALUATE_DBENTRY: {
     877           3 :         const DBEntry *entry = req->db_entry();
     878           3 :         HandleRevaluateDBEntry(entry, flow, active_flow, deleted_flow);
     879           3 :         break;
     880             :     }
     881             : 
     882          33 :     case FlowEvent::RECOMPUTE_FLOW: {
     883          33 :         if (active_flow)
     884          27 :             RecomputeFlow(flow);
     885          33 :         break;
     886             :     }
     887             : 
     888             :     // Check if flow-handle changed. This can happen if vrouter tries to
     889             :     // setup the flow which was evicted earlier
     890           0 :     case FlowEvent::EVICT_FLOW: {
     891           0 :         if (flow->flow_handle() != req->flow_handle() ||
     892           0 :             flow->gen_id() != req->gen_id())
     893           0 :             break;
     894           0 :         EvictFlow(flow, rflow, req->evict_gen_id());
     895           0 :         break;
     896             :     }
     897             : 
     898         144 :     case FlowEvent::KSYNC_EVENT: {
     899         144 :         const FlowEventKSync *ksync_event =
     900             :             static_cast<const FlowEventKSync *>(req);
     901             :         // Handle vr_flow message
     902         144 :         ProcessKSyncFlowEvent(ksync_event, flow);
     903             :         // Handle vr_response message
     904             :         // Trigger the ksync flow event to move ksync state-machine
     905             :         KSyncFlowIndexManager *imgr =
     906         144 :             agent()->ksync()->ksync_flow_index_manager();
     907             :         FlowTableKSyncEntry *ksync_entry = static_cast<FlowTableKSyncEntry *>
     908         144 :             (ksync_event->ksync_entry());
     909         144 :         imgr->TriggerKSyncEvent(ksync_entry, ksync_event->ksync_event());
     910         144 :         break;
     911             :     }
     912             : 
     913           0 :     case FlowEvent::UNRESOLVED_FLOW_ENTRY: {
     914           0 :         if (flow->deleted()) {
     915           0 :             break;
     916             :         }
     917             : 
     918           0 :         if (flow->GetMaxRetryAttempts() < FlowEntry::kFlowRetryAttempts) {
     919           0 :             flow->IncrementRetrycount();
     920             :         } else {
     921           0 :             flow->MakeShortFlow(FlowEntry::SHORT_NO_MIRROR_ENTRY);
     922           0 :             flow->ResetRetryCount();
     923             :         }
     924             : 
     925           0 :         UpdateKSync(flow, true);
     926           0 :         break;
     927             :     }
     928             : 
     929           0 :     default: {
     930           0 :         assert(0);
     931             :         break;
     932             :     }
     933             :     }
     934         217 :     return true;
     935         217 : }
     936             : 
     937         167 : void FlowTable::GetFlowSandeshActionParams(const FlowAction &action_info,
     938             :                                            std::string &action_str) {
     939         167 :     std::bitset<32> bs(action_info.action);
     940        5511 :     for (unsigned int i = 0; i < bs.size(); i++) {
     941        5344 :         if (bs[i]) {
     942         133 :             if (!action_str.empty()) {
     943           9 :                 action_str += "|";
     944             :             }
     945         266 :             action_str += TrafficAction::ActionToString(
     946         133 :                 static_cast<TrafficAction::Action>(i));
     947             :         }
     948             :     }
     949         167 : }
     950             : /////////////////////////////////////////////////////////////////////////////
     951             : // FlowEntryFreeList implementation
     952             : /////////////////////////////////////////////////////////////////////////////
     953           0 : void FlowTable::GrowFreeList() {
     954           0 :     free_list_.Grow();
     955           0 :     ksync_object_->GrowFreeList();
     956           0 : }
     957             : 
     958           1 : FlowEntryFreeList::FlowEntryFreeList(FlowTable *table) :
     959           1 :     table_(table), max_count_(0), grow_pending_(false), total_alloc_(0),
     960           1 :     total_free_(0), free_list_() {
     961           1 :     uint32_t count = kInitCount;
     962           1 :     if (table->agent()->test_mode()) {
     963           1 :         count = kTestInitCount;
     964             :     }
     965             : 
     966        5001 :     while (max_count_ < count) {
     967        5000 :         free_list_.push_back(*new FlowEntry(table));
     968        5000 :         max_count_++;
     969             :     }
     970           1 : }
     971             : 
     972           1 : FlowEntryFreeList::~FlowEntryFreeList() {
     973        5001 :     while (free_list_.empty() == false) {
     974        5000 :         FreeList::iterator it = free_list_.begin();
     975        5000 :         FlowEntry *flow = &(*it);
     976       10000 :         free_list_.erase(it);
     977        5000 :         delete flow;
     978             :     }
     979           1 : }
     980             : 
     981             : // Allocate a chunk of FlowEntries
     982           0 : void FlowEntryFreeList::Grow() {
     983           0 :     assert(table_->ConcurrencyCheck(table_->flow_task_id()) == true);
     984           0 :     grow_pending_ = false;
     985           0 :     if (free_list_.size() >= kMinThreshold)
     986           0 :         return;
     987             : 
     988           0 :     for (uint32_t i = 0; i < kGrowSize; i++) {
     989           0 :         free_list_.push_back(*new FlowEntry(table_));
     990           0 :         max_count_++;
     991             :     }
     992             : }
     993             : 
     994          94 : FlowEntry *FlowEntryFreeList::Allocate(const FlowKey &key) {
     995          94 :     assert(table_->ConcurrencyCheck(table_->flow_task_id()) == true);
     996          94 :     FlowEntry *flow = NULL;
     997          94 :     if (free_list_.size() == 0) {
     998           0 :         flow = new FlowEntry(table_);
     999           0 :         max_count_++;
    1000             :     } else {
    1001          94 :         FreeList::iterator it = free_list_.begin();
    1002          94 :         flow = &(*it);
    1003         188 :         free_list_.erase(it);
    1004             :     }
    1005             : 
    1006          94 :     if (grow_pending_ == false && free_list_.size() < kMinThreshold) {
    1007           0 :         grow_pending_ = true;
    1008           0 :         FlowProto *proto = table_->agent()->pkt()->get_flow_proto();
    1009           0 :         proto->GrowFreeListRequest(table_);
    1010             :     }
    1011          94 :     flow->Reset(key);
    1012          94 :     total_alloc_++;
    1013          94 :     return flow;
    1014             : }
    1015             : 
    1016          94 : void FlowEntryFreeList::Free(FlowEntry *flow) {
    1017             :    // only flow logging task and flow event task can  free up the flow entry ,
    1018          94 :    assert((table_->ConcurrencyCheck(table_->flow_task_id()) == true) ||
    1019             :         (table_->ConcurrencyCheck(
    1020             :             table_->flow_logging_task_id(), false) == true));
    1021          94 :     total_free_++;
    1022          94 :     flow->Reset();
    1023          94 :     if (free_list_.size() < kMaxThreshold) {
    1024          94 :         free_list_.push_back(*flow);
    1025          94 :         assert(flow->flow_mgmt_info() == NULL);
    1026             :     } else {
    1027           0 :         delete flow;
    1028           0 :         --max_count_;
    1029             :     }
    1030          94 : }

Generated by: LCOV version 1.14