From 1bb5070775f7f6f47a06cc00c7396679b840642d Mon Sep 17 00:00:00 2001 From: Stephen Sun <5379172+stephenxs@users.noreply.github.com> Date: Sat, 18 Jun 2022 00:15:24 +0800 Subject: [PATCH] Enhance mock test for dynamic buffer manager for port removing and qos reload flows (#2262) What I did Enhance the mock test of the dynamic buffer manager in port remove and config qos clear flow and fix bugs during mock test implementation Implement mock method ProduceStateTable::del Signed-off-by: Stephen Sun stephens@nvidia.com How I verified it Run regression test, mock test, vs test, and manual test. Details if related 1. Support mock test for dynamic buffer manager config qos clear and reclaiming buffer Remove port 2. Handle port remove/create flow Cache cable length for a port Try reclaiming unused buffer when maximum buffer parameters are received for a port whose state is ADMIN_DOWN and m_bufferCompletelyInitialized is true 3. Handle config qos clear If all buffer pools are removed when m_bufferPoolReady is true, remove all zero pools and profiles. Reload zero profiles and pools if they have not been loaded when reclaiming buffer --- cfgmgr/buffermgrdyn.cpp | 114 +++++++ cfgmgr/buffermgrdyn.h | 2 + tests/mock_tests/buffermgrdyn_ut.cpp | 486 +++++++++++++++++++++++++++ tests/mock_tests/mock_table.cpp | 8 + 4 files changed, 610 insertions(+) diff --git a/cfgmgr/buffermgrdyn.cpp b/cfgmgr/buffermgrdyn.cpp index 1c5b99a6f8..5017ad9d1b 100644 --- a/cfgmgr/buffermgrdyn.cpp +++ b/cfgmgr/buffermgrdyn.cpp @@ -1862,6 +1862,14 @@ task_process_status BufferMgrDynamic::handleBufferMaxParam(KeyOpFieldsValuesTupl SWSS_LOG_INFO("BUFFER_MAX_PARAM: Got port %s's max priority group %s", key.c_str(), value.c_str()); portInfo.maximum_buffer_objects[BUFFER_PG] = (sai_uint32_t)pgCount; + + if (m_bufferCompletelyInitialized && portInfo.state == PORT_ADMIN_DOWN) + { + // This is mostly for the case where the port is created only-the-fly + // The maximum buffer parameters can be received after buffer items + reclaimReservedBufferForPort(key, m_portPgLookup, BUFFER_PG); + SWSS_LOG_NOTICE("Admin-down port %s is handled after maximum buffer parameter has been received", key.c_str()); + } } else if (fvField(i) == "max_queues") { @@ -1875,6 +1883,14 @@ task_process_status BufferMgrDynamic::handleBufferMaxParam(KeyOpFieldsValuesTupl SWSS_LOG_INFO("BUFFER_MAX_PARAM: Got port %s's max queue %s", key.c_str(), value.c_str()); portInfo.maximum_buffer_objects[BUFFER_QUEUE] = (sai_uint32_t)queueCount; + + if (m_bufferCompletelyInitialized && portInfo.state == PORT_ADMIN_DOWN) + { + // This is mostly for the case where the port is created only-the-fly + // The maximum buffer parameters can be received after buffer items + reclaimReservedBufferForPort(key, m_portQueueLookup, BUFFER_QUEUE); + SWSS_LOG_NOTICE("Admin-down port %s is handled after maximum buffer parameter has been received", key.c_str()); + } } } } @@ -1961,6 +1977,7 @@ task_process_status BufferMgrDynamic::handleCableLenTable(KeyOpFieldsValuesTuple int failed_item_count = 0; if (op == SET_COMMAND) { + m_cableLengths.clear(); for (auto i : kfvFieldsValues(tuple)) { // receive and cache cable length table @@ -1975,6 +1992,8 @@ task_process_status BufferMgrDynamic::handleCableLenTable(KeyOpFieldsValuesTuple port.c_str(), portInfo.effective_speed.c_str(), portInfo.cable_length.c_str(), portInfo.gearbox_model.c_str()); + m_cableLengths[port] = cable_length; + if (portInfo.cable_length == cable_length) { continue; @@ -2183,6 +2202,11 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu string &mtu = portInfo.mtu; string &effective_speed = portInfo.effective_speed; + if (cable_length.empty() && !m_cableLengths[port].empty()) + { + cable_length = m_cableLengths[port]; + } + bool need_refresh_all_buffer_objects = false, need_handle_admin_down = false, was_admin_down = false; if (effective_speed_updated || mtu_updated) @@ -2304,6 +2328,28 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu task_status = refreshPgsForPort(port, portInfo.effective_speed, portInfo.cable_length, portInfo.mtu); } } + else if (op == DEL_COMMAND) + { + cleanUpItemsForReclaimingBuffer(port); + if ((m_portPgLookup.find(port) != m_portPgLookup.end() + && !m_portPgLookup[port].empty()) + || (m_portQueueLookup.find(port) != m_portQueueLookup.end() + && !m_portQueueLookup[port].empty()) + || (m_portProfileListLookups[BUFFER_INGRESS].find(port) != m_portProfileListLookups[BUFFER_INGRESS].end() + && !m_portProfileListLookups[BUFFER_INGRESS][port].empty()) + || (m_portProfileListLookups[BUFFER_EGRESS].find(port) != m_portProfileListLookups[BUFFER_EGRESS].end() + && !m_portProfileListLookups[BUFFER_EGRESS][port].empty())) + { + SWSS_LOG_INFO("Port %s can't be removed before buffer items have been removed", port.c_str()); + return task_process_status::task_need_retry; + } + m_portPgLookup.erase(port); + m_portQueueLookup.erase(port); + m_portProfileListLookups[BUFFER_INGRESS].erase(port); + m_portProfileListLookups[BUFFER_EGRESS].erase(port); + m_portInfoLookup.erase(port); + SWSS_LOG_NOTICE("Port %s is removed", port.c_str()); + } return task_status; } @@ -2401,6 +2447,28 @@ task_process_status BufferMgrDynamic::handleBufferPoolTable(KeyOpFieldsValuesTup m_applBufferPoolTable.del(pool); m_stateBufferPoolTable.del(pool); m_bufferPoolLookup.erase(pool); + if (pool == INGRESS_LOSSLESS_PG_POOL_NAME) + { + m_configuredSharedHeadroomPoolSize.clear(); + } + + if (m_bufferPoolReady && m_bufferPoolLookup.empty()) + { + for(auto &port : m_adminDownPorts) + { + cleanUpItemsForReclaimingBuffer(port); + } + + // Zero profiles must be unloaded once all pools have been uploaded + // This can be resulted from "config qos reload" + // Any zero profile left can leads to buffer pool not able to be cleared + unloadZeroPoolAndProfiles(); + + m_bufferPoolReady = false; + m_bufferCompletelyInitialized = false; + + m_pendingApplyZeroProfilePorts = m_adminDownPorts; + } } else { @@ -2634,6 +2702,12 @@ void BufferMgrDynamic::handleSetSingleBufferObjectOnAdminDownPort(buffer_directi { if (idsToZero.empty()) { + // Happens only after "config qos reload" + if (!m_zeroProfilesLoaded) + { + loadZeroPoolAndProfiles(); + } + // If initialization finished, no extra handle required. // Check whether the key overlaps with supported but not configured map auto const &idsToAdd = parseObjectNameFromKey(key, 1); @@ -2749,6 +2823,14 @@ void BufferMgrDynamic::handleDelSingleBufferObjectOnAdminDownPort(buffer_directi if (idsToZero.empty()) { + if (!m_bufferPoolReady) + { + // Reclaiming buffer has not started yet so just remove it. + // Do not add it to "supported but not configured" set + updateBufferObjectToDb(key, "", false, direction); + return; + } + // For admin down ports, if zero profiles have been applied to all configured items // do NOT remove it otherwise SDK default value will be set for the items // Move the key to supported_but_not_configured_items so that the slice of items @@ -3125,6 +3207,22 @@ task_process_status BufferMgrDynamic::handleSingleBufferPortProfileListEntry(con // For admin-down ports, zero profile list has been applied on the port when it entered admin-down state updateBufferObjectListToDb(key, profileListLookup[port], dir); } + else + { + const auto &profileList = m_portProfileListLookups[dir][port]; + if (!profileList.empty()) + { + // Happens only after "config qos reload" + if (!m_zeroProfilesLoaded) + { + loadZeroPoolAndProfiles(); + } + vector fvVector; + const string &zeroProfileNameList = constructZeroProfileListFromNormalProfileList(profileList, port); + fvVector.emplace_back(buffer_profile_list_field_name, zeroProfileNameList); + m_applBufferProfileListTables[dir].set(port, fvVector); + } + } } else if (op == DEL_COMMAND) { @@ -3462,9 +3560,25 @@ void BufferMgrDynamic::handlePendingBufferObjects() } } +void BufferMgrDynamic::cleanUpItemsForReclaimingBuffer(const string &port) +{ + // Clean up zero buffers when the buffer pools or a port has been removed + if (!m_bufferObjectIdsToZero[BUFFER_PG].empty()) + { + updateBufferObjectToDb(port + delimiter + m_bufferObjectIdsToZero[BUFFER_PG], "", false, BUFFER_PG); + } + if (!m_bufferObjectIdsToZero[BUFFER_QUEUE].empty()) + { + updateBufferObjectToDb(port + delimiter + m_bufferObjectIdsToZero[BUFFER_QUEUE], "", false, BUFFER_QUEUE); + } + removeSupportedButNotConfiguredItemsOnPort(m_portInfoLookup[port], port); +} + void BufferMgrDynamic::doTask(SelectableTimer &timer) { checkSharedBufferPoolSize(true); if (!m_bufferCompletelyInitialized) + { handlePendingBufferObjects(); + } } diff --git a/cfgmgr/buffermgrdyn.h b/cfgmgr/buffermgrdyn.h index cb94227522..11b55d7667 100644 --- a/cfgmgr/buffermgrdyn.h +++ b/cfgmgr/buffermgrdyn.h @@ -196,6 +196,7 @@ class BufferMgrDynamic : public Orch // key: port name // updated only when a port's speed and cable length updated port_info_lookup_t m_portInfoLookup; + std::map m_cableLengths; std::set m_adminDownPorts; std::set m_pendingApplyZeroProfilePorts; std::set m_pendingSupportedButNotConfiguredPorts[BUFFER_DIR_MAX]; @@ -302,6 +303,7 @@ class BufferMgrDynamic : public Orch void handleSetSingleBufferObjectOnAdminDownPort(buffer_direction_t direction, const std::string &port, const std::string &key, const std::string &profile); void handleDelSingleBufferObjectOnAdminDownPort(buffer_direction_t direction, const std::string &port, const std::string &key, port_info_t &portInfo); bool isReadyToReclaimBufferOnPort(const std::string &port); + void cleanUpItemsForReclaimingBuffer(const std::string &port); // Main flows template task_process_status reclaimReservedBufferForPort(const std::string &port, T &obj, buffer_direction_t dir); diff --git a/tests/mock_tests/buffermgrdyn_ut.cpp b/tests/mock_tests/buffermgrdyn_ut.cpp index b64a367c79..9dd17a5da8 100644 --- a/tests/mock_tests/buffermgrdyn_ut.cpp +++ b/tests/mock_tests/buffermgrdyn_ut.cpp @@ -785,6 +785,492 @@ namespace buffermgrdyn_test } } + /* + * Clear qos with reclaiming buffer + * + * To test clear qos flow with reclaiming buffer. + * 1. Init buffer manager as normal + * 2. Configure buffer for 2 ports with admin status being up and down respectively + * 3. Clear qos + * 4. Check whether all the buffer items have been removed + * 5. Repeat the flow from step 2 for two extra times: + * - Check whether buffer manager works correctly after clear qos + * - STATE_DB.BUFFER_MAX_PARAM is received before and after buffer items received + */ + TEST_F(BufferMgrDynTest, BufferMgrTestClearQosReclaimingBuffer) + { + vector fieldValues; + vector keys; + vector skippedPools = {"", "ingress_lossless_pool", ""}; + int round = 0; + + SetUpReclaimingBuffer(); + shared_ptr> zero_profile = make_shared>(zeroProfile); + + InitDefaultLosslessParameter(); + InitMmuSize(); + + StartBufferManager(zero_profile); + + statePortTable.set("Ethernet0", + { + {"supported_speeds", "100000,50000,40000,25000,10000,1000"} + }); + InitPort("Ethernet0", "down"); + InitPort("Ethernet4", "down"); + InitPort("Ethernet6", "down"); + InitPort("Ethernet8", "down"); + vector adminDownPorts = {"Ethernet0", "Ethernet4", "Ethernet6"}; + vector ports = {"Ethernet0", "Ethernet2", "Ethernet4", "Ethernet6"}; + InitPort("Ethernet2"); + InitCableLength("Ethernet2", "5m"); + auto expectedProfile = "pg_lossless_100000_5m_profile"; + ASSERT_EQ(m_dynamicBuffer->m_portInfoLookup["Ethernet0"].state, PORT_ADMIN_DOWN); + + SetPortInitDone(); + for(auto &skippedPool : skippedPools) + { + // Call timer + m_dynamicBuffer->doTask(m_selectableTable); + ASSERT_EQ(m_dynamicBuffer->m_bufferPoolLookup.size(), 0); + InitBufferPool(); + ASSERT_EQ(m_dynamicBuffer->m_bufferPoolLookup.size(), 3); + appBufferPoolTable.getKeys(keys); + ASSERT_EQ(keys.size(), 3); + for (auto i : testBufferPool) + { + CheckPool(m_dynamicBuffer->m_bufferPoolLookup[i.first], testBufferPool[i.first]); + fieldValues.clear(); + appBufferPoolTable.get(i.first, fieldValues); + CheckPool(m_dynamicBuffer->m_bufferPoolLookup[i.first], fieldValues); + } + + InitDefaultBufferProfile(); + appBufferProfileTable.getKeys(keys); + ASSERT_EQ(keys.size(), 3); + ASSERT_EQ(m_dynamicBuffer->m_bufferProfileLookup.size(), 3); + for (auto i : testBufferProfile) + { + CheckProfile(m_dynamicBuffer->m_bufferProfileLookup[i.first], testBufferProfile[i.first]); + fieldValues.clear(); + appBufferProfileTable.get(i.first, fieldValues); + CheckProfile(m_dynamicBuffer->m_bufferProfileLookup[i.first], fieldValues); + } + + for (auto &adminDownPort : adminDownPorts) + { + InitBufferPg(adminDownPort + "|3-4", "NULL"); + InitBufferQueue(adminDownPort + "|3-4", "egress_lossless_profile"); + InitBufferQueue(adminDownPort + "|0-2", "egress_lossy_profile"); + InitBufferQueue(adminDownPort + "|5-6", "egress_lossy_profile"); + } + InitBufferPg("Ethernet0|0", "ingress_lossy_profile"); + InitBufferPg("Ethernet0|3-4"); + InitBufferProfileList("Ethernet0", "ingress_lossless_profile", bufferIngProfileListTable); + InitBufferProfileList("Ethernet0", "egress_lossless_profile,egress_lossy_profile", bufferEgrProfileListTable); + + // Init buffer items for a normal port and check APPL_DB + InitBufferQueue("Ethernet2|3-4", "egress_lossless_profile"); + InitBufferQueue("Ethernet2|0-2", "egress_lossy_profile"); + InitBufferPg("Ethernet2|3-4"); + InitBufferProfileList("Ethernet2", "ingress_lossless_profile", bufferIngProfileListTable); + InitBufferProfileList("Ethernet2", "egress_lossless_profile,egress_lossy_profile", bufferEgrProfileListTable); + + fieldValues.clear(); + ASSERT_TRUE(appBufferPgTable.get("Ethernet2:3-4", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile", expectedProfile}}); + fieldValues.clear(); + ASSERT_TRUE(appBufferQueueTable.get("Ethernet2:0-2", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile", "egress_lossy_profile"}}); + fieldValues.clear(); + ASSERT_TRUE(appBufferQueueTable.get("Ethernet2:3-4", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile", "egress_lossless_profile"}}); + fieldValues.clear(); + ASSERT_TRUE(appBufferIngProfileListTable.get("Ethernet2", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile_list", "ingress_lossless_profile"}}); + fieldValues.clear(); + ASSERT_TRUE(appBufferEgrProfileListTable.get("Ethernet2", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile_list", "egress_lossless_profile,egress_lossy_profile"}}); + + // Buffer pools ready but the port is not ready to be reclaimed + m_dynamicBuffer->doTask(m_selectableTable); + + // Push maximum buffer parameters for the port in order to make it ready to reclaim + if (round == 0) + { + // To simulate different sequences + // The 1st round: STATE_DB.PORT_TABLE is updated after buffer items ready + // The 2nd, 3rd rounds: before + + for (auto &adminDownPort : adminDownPorts) + { + stateBufferTable.set(adminDownPort, + { + {"max_priority_groups", "8"}, + {"max_queues", "16"} + }); + } + stateBufferTable.set("Ethernet8", + { + {"max_priority_groups", "8"}, + {"max_queues", "16"} + }); + m_dynamicBuffer->addExistingData(&stateBufferTable); + static_cast(m_dynamicBuffer)->doTask(); + } + + m_dynamicBuffer->doTask(m_selectableTable); + + // Check whether zero profiles and pool have been applied + appBufferPoolTable.getKeys(keys); + ASSERT_EQ(keys.size(), 4); + for (auto key : keys) + { + if (testBufferPool.find(key) == testBufferPool.end()) + { + fieldValues.clear(); + appBufferPoolTable.get(key, fieldValues); + CheckIfVectorsMatch(fieldValues, zeroProfileMap[key]); + } + } + + appBufferProfileTable.getKeys(keys); + for (auto key : keys) + { + if (testBufferProfile.find(key) == testBufferProfile.end()) + { + fieldValues.clear(); + appBufferProfileTable.get(key, fieldValues); + if (zeroProfileMap.find(key) == zeroProfileMap.end()) + CheckIfVectorsMatch(fieldValues, + { + {"xon", ""}, // Due to the limitation of mock lua scricpt call, + {"xoff", ""}, // we can not calculate the number + {"size", ""}, // so expected value is the empty string + {"pool", "ingress_lossless_pool"}, + {"dynamic_th", "0"} + }); + else + CheckIfVectorsMatch(fieldValues, zeroProfileMap[key]); + } + } + + for (auto &adminDownPort : adminDownPorts) + { + fieldValues.clear(); + ASSERT_TRUE(appBufferPgTable.get("Ethernet0:0", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile", "ingress_lossy_pg_zero_profile"}}); + ASSERT_FALSE(appBufferPgTable.get("Ethernet0:3-4", fieldValues)); + fieldValues.clear(); + ASSERT_TRUE(appBufferQueueTable.get(adminDownPort + ":0-2", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile", "egress_lossy_zero_profile"}}); + fieldValues.clear(); + ASSERT_TRUE(appBufferQueueTable.get(adminDownPort + ":3-4", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile", "egress_lossless_zero_profile"}}); + fieldValues.clear(); + ASSERT_TRUE(appBufferQueueTable.get(adminDownPort + ":5-6", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile", "egress_lossy_zero_profile"}}); + fieldValues.clear(); + } + ASSERT_TRUE(appBufferIngProfileListTable.get("Ethernet0", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile_list", "ingress_lossless_zero_profile"}}); + fieldValues.clear(); + ASSERT_TRUE(appBufferEgrProfileListTable.get("Ethernet0", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile_list", "egress_lossless_zero_profile,egress_lossy_zero_profile"}}); + + // Configured but not applied items. There is an extra delay + m_dynamicBuffer->m_waitApplyAdditionalZeroProfiles = 0; + m_dynamicBuffer->doTask(m_selectableTable); + for (auto &adminDownPort : adminDownPorts) + { + ASSERT_TRUE(appBufferQueueTable.get(adminDownPort + ":7-15", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile", "egress_lossy_zero_profile"}}); + fieldValues.clear(); + } + + if (round == 0) + { + ASSERT_TRUE(appBufferQueueTable.get("Ethernet8:0-15", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile", "egress_lossy_zero_profile"}}); + fieldValues.clear(); + ASSERT_TRUE(appBufferPgTable.get("Ethernet8:0", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile", "ingress_lossy_pg_zero_profile"}}); + fieldValues.clear(); + ClearBufferObject("Ethernet8", CFG_PORT_TABLE_NAME); + ASSERT_FALSE(appBufferPgTable.get("Ethernet8:0", fieldValues)); + ASSERT_FALSE(appBufferQueueTable.get("Ethernet8:0-15", fieldValues)); + } + + ClearBufferObject("Ethernet0|3-4", CFG_BUFFER_QUEUE_TABLE_NAME); + ClearBufferObject("Ethernet4|5-6", CFG_BUFFER_QUEUE_TABLE_NAME); + ClearBufferObject("Ethernet4|0-2", CFG_BUFFER_QUEUE_TABLE_NAME); + // Clear all qos tables + ClearBufferPool(skippedPool); + ClearBufferProfile(); + ClearBufferObject("Ethernet0|0", CFG_BUFFER_PG_TABLE_NAME); + for (auto &adminDownPort : adminDownPorts) + { + ClearBufferObject(adminDownPort + "|3-4", CFG_BUFFER_PG_TABLE_NAME); + } + ClearBufferObject("Ethernet2|3-4", CFG_BUFFER_PG_TABLE_NAME); + ClearBufferObject("Ethernet0|0-2", CFG_BUFFER_QUEUE_TABLE_NAME); + ClearBufferObject("Ethernet2|0-2", CFG_BUFFER_QUEUE_TABLE_NAME); + ClearBufferObject("Ethernet2|3-4", CFG_BUFFER_QUEUE_TABLE_NAME); + ClearBufferObject("Ethernet0|5-6", CFG_BUFFER_QUEUE_TABLE_NAME); + ClearBufferObject("Ethernet4|3-4", CFG_BUFFER_QUEUE_TABLE_NAME); + ClearBufferObject("Ethernet6|0-2", CFG_BUFFER_QUEUE_TABLE_NAME); + ClearBufferObject("Ethernet6|3-4", CFG_BUFFER_QUEUE_TABLE_NAME); + ClearBufferObject("Ethernet6|5-6", CFG_BUFFER_QUEUE_TABLE_NAME); + for (auto &port : ports) + { + ClearBufferObject(port, CFG_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME); + ClearBufferObject(port, CFG_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME); + } + + // Run timer + m_dynamicBuffer->doTask(m_selectableTable); + + if (!skippedPool.empty()) + { + // Clear the pool that was skipped in the previous step + // This is to simulate the case where all the pools are not removed in one-shot + ClearBufferPool("", skippedPool); + m_dynamicBuffer->doTask(m_selectableTable); + } + + // All internal data and APPL_DB has been cleared + ASSERT_TRUE((appBufferPgTable.getKeys(keys), keys.empty())); + ASSERT_TRUE((appBufferQueueTable.getKeys(keys), keys.empty())); + ASSERT_TRUE((appBufferProfileTable.getKeys(keys), keys.empty())); + ASSERT_TRUE((appBufferPoolTable.getKeys(keys), keys.empty())); + ASSERT_TRUE((appBufferIngProfileListTable.getKeys(keys), keys.empty())); + ASSERT_TRUE((appBufferEgrProfileListTable.getKeys(keys), keys.empty())); + ASSERT_TRUE(m_dynamicBuffer->m_bufferPoolLookup.empty()); + ASSERT_TRUE(m_dynamicBuffer->m_bufferProfileLookup.empty()); + ASSERT_TRUE(m_dynamicBuffer->m_portPgLookup.empty()); + ASSERT_TRUE(m_dynamicBuffer->m_portQueueLookup.empty()); + ASSERT_TRUE(m_dynamicBuffer->m_portProfileListLookups[BUFFER_EGRESS].empty()); + ASSERT_TRUE(m_dynamicBuffer->m_portProfileListLookups[BUFFER_INGRESS].empty()); + + round++; + } + } + + + /* + * Clear qos with reclaiming buffer sad flows + * Reclaiming buffer should be triggered via any single buffer item + */ + TEST_F(BufferMgrDynTest, BufferMgrTestReclaimingBufferSadFlows) + { + vector fieldValues; + vector keys; + vector> bufferItems; + + bufferItems.emplace_back(bufferPgTable, "Ethernet0:0", "ingress_lossy_profile", appBufferPgTable, "profile", "ingress_lossy_pg_zero_profile"); + bufferItems.emplace_back(bufferPgTable, "Ethernet0:3-4", "NULL", appBufferPgTable, "", ""); + bufferItems.emplace_back(bufferQueueTable, "Ethernet0:0-2", "egress_lossy_profile", appBufferQueueTable, "profile", "egress_lossy_zero_profile"); + bufferItems.emplace_back(bufferQueueTable, "Ethernet0:3-4", "egress_lossless_profile", appBufferQueueTable, "profile", "egress_lossless_zero_profile"); + bufferItems.emplace_back(bufferIngProfileListTable, "Ethernet0", "ingress_lossless_profile", appBufferIngProfileListTable, "profile_list", "ingress_lossless_zero_profile"); + bufferItems.emplace_back(bufferEgrProfileListTable, "Ethernet0", "egress_lossless_profile,egress_lossy_profile", appBufferEgrProfileListTable, "profile_list", "egress_lossless_zero_profile,egress_lossy_zero_profile"); + + SetUpReclaimingBuffer(); + shared_ptr> zero_profile = make_shared>(zeroProfile); + + InitDefaultLosslessParameter(); + InitMmuSize(); + + StartBufferManager(zero_profile); + + stateBufferTable.set("Ethernet0", + { + {"max_priority_groups", "8"}, + {"max_queues", "16"} + }); + m_dynamicBuffer->addExistingData(&stateBufferTable); + static_cast(m_dynamicBuffer)->doTask(); + + InitPort("Ethernet0", "down"); + + ASSERT_EQ(m_dynamicBuffer->m_portInfoLookup["Ethernet0"].state, PORT_ADMIN_DOWN); + + SetPortInitDone(); + m_dynamicBuffer->doTask(m_selectableTable); + + // After "config qos clear" the zero buffer profiles are unloaded + m_dynamicBuffer->unloadZeroPoolAndProfiles(); + + // Starts with empty buffer tables + for(auto &bufferItem : bufferItems) + { + auto &cfgTable = get<0>(bufferItem); + auto &key = get<1>(bufferItem); + auto &profile = get<2>(bufferItem); + auto &appTable = get<3>(bufferItem); + auto &fieldName = get<4>(bufferItem); + auto &expectedProfile = get<5>(bufferItem); + + cfgTable.set(key, + { + {fieldName, profile} + }); + m_dynamicBuffer->addExistingData(&cfgTable); + static_cast(m_dynamicBuffer)->doTask(); + + ASSERT_FALSE(m_dynamicBuffer->m_bufferCompletelyInitialized); + ASSERT_FALSE(m_dynamicBuffer->m_zeroProfilesLoaded); + ASSERT_TRUE(m_dynamicBuffer->m_portInitDone); + ASSERT_TRUE(m_dynamicBuffer->m_pendingApplyZeroProfilePorts.find("Ethernet0") != m_dynamicBuffer->m_pendingApplyZeroProfilePorts.end()); + + InitBufferPool(); + InitDefaultBufferProfile(); + + m_dynamicBuffer->doTask(m_selectableTable); + + // Another doTask to ensure all the dependent tables have been drained + // after buffer pools and profiles have been drained + static_cast(m_dynamicBuffer)->doTask(); + + if (expectedProfile.empty()) + { + ASSERT_FALSE(appTable.get(key, fieldValues)); + } + else + { + ASSERT_TRUE(appTable.get(key, fieldValues)); + CheckIfVectorsMatch(fieldValues, {{fieldName, expectedProfile}}); + } + + m_dynamicBuffer->m_waitApplyAdditionalZeroProfiles = 0; + m_dynamicBuffer->doTask(m_selectableTable); + + ASSERT_TRUE(m_dynamicBuffer->m_pendingApplyZeroProfilePorts.empty()); + ASSERT_TRUE(m_dynamicBuffer->m_bufferCompletelyInitialized); + + // Simulate clear qos + ClearBufferPool(); + ClearBufferProfile(); + + // Call timer + m_dynamicBuffer->doTask(m_selectableTable); + } + } + + /* + * Port removing flow + */ + TEST_F(BufferMgrDynTest, BufferMgrTestRemovePort) + { + vector fieldValues; + vector keys; + vector statuses = {"up", "down"}; + + // Prepare information that will be read at the beginning + InitDefaultLosslessParameter(); + InitMmuSize(); + + shared_ptr> zero_profile = make_shared>(zeroProfile); + StartBufferManager(zero_profile); + + SetPortInitDone(); + // Timer will be called + m_dynamicBuffer->doTask(m_selectableTable); + + InitBufferPool(); + appBufferPoolTable.getKeys(keys); + ASSERT_EQ(keys.size(), 3); + InitDefaultBufferProfile(); + appBufferProfileTable.getKeys(keys); + ASSERT_EQ(keys.size(), 3); + ASSERT_EQ(m_dynamicBuffer->m_bufferProfileLookup.size(), 3); + + m_dynamicBuffer->m_bufferCompletelyInitialized = true; + m_dynamicBuffer->m_waitApplyAdditionalZeroProfiles = 0; + InitCableLength("Ethernet0", "5m"); + + for(auto status : statuses) + { + bool admin_up = (status == "up"); + + InitPort("Ethernet0", status); + ASSERT_TRUE(m_dynamicBuffer->m_portInfoLookup.find("Ethernet0") != m_dynamicBuffer->m_portInfoLookup.end()); + ASSERT_EQ(m_dynamicBuffer->m_portInfoLookup["Ethernet0"].state, admin_up ? PORT_READY : PORT_ADMIN_DOWN); + + // Init port buffer items + InitBufferQueue("Ethernet0|3-4", "egress_lossless_profile"); + InitBufferProfileList("Ethernet0", "ingress_lossless_profile", bufferIngProfileListTable); + InitBufferPg("Ethernet0|3-4"); + if (admin_up) + { + InitBufferProfileList("Ethernet0", "egress_lossless_profile,egress_lossy_profile", bufferEgrProfileListTable); + + auto expectedProfile = "pg_lossless_100000_5m_profile"; + CheckPg("Ethernet0", "Ethernet0:3-4", expectedProfile); + CheckQueue("Ethernet0", "Ethernet0:3-4", "egress_lossless_profile", true); + CheckProfileList("Ethernet0", true, "ingress_lossless_profile"); + CheckProfileList("Ethernet0", false, "egress_lossless_profile,egress_lossy_profile"); + } + else + { + InitBufferPg("Ethernet0|0", "ingress_lossy_profile"); + + stateBufferTable.set("Ethernet0", + { + {"max_priority_groups", "8"}, + {"max_queues", "16"} + }); + m_dynamicBuffer->addExistingData(&stateBufferTable); + static_cast(m_dynamicBuffer)->doTask(); + + // Make sure profile list is applied after maximum buffer parameter table + InitBufferProfileList("Ethernet0", "egress_lossless_profile,egress_lossy_profile", bufferEgrProfileListTable); + + fieldValues.clear(); + ASSERT_TRUE(appBufferPgTable.get("Ethernet0:0", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile", "ingress_lossy_pg_zero_profile"}}); + + fieldValues.clear(); + ASSERT_TRUE(appBufferQueueTable.get("Ethernet0:3-4", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile", "egress_lossless_zero_profile"}}); + + fieldValues.clear(); + ASSERT_TRUE(appBufferQueueTable.get("Ethernet0:0-2", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile", "egress_lossy_zero_profile"}}); + + fieldValues.clear(); + ASSERT_TRUE(appBufferQueueTable.get("Ethernet0:5-15", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile", "egress_lossy_zero_profile"}}); + + fieldValues.clear(); + ASSERT_TRUE(appBufferIngProfileListTable.get("Ethernet0", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile_list", "ingress_lossless_zero_profile"}}); + + fieldValues.clear(); + ASSERT_TRUE(appBufferEgrProfileListTable.get("Ethernet0", fieldValues)); + CheckIfVectorsMatch(fieldValues, {{"profile_list", "egress_lossless_zero_profile,egress_lossy_zero_profile"}}); + + ClearBufferObject("Ethernet0|0", CFG_BUFFER_PG_TABLE_NAME); + } + + // Remove port + ClearBufferObject("Ethernet0", CFG_PORT_TABLE_NAME); + ASSERT_FALSE(m_dynamicBuffer->m_portPgLookup.empty()); + ClearBufferObject("Ethernet0", CFG_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME); + ClearBufferObject("Ethernet0", CFG_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME); + ClearBufferObject("Ethernet0|3-4", CFG_BUFFER_PG_TABLE_NAME); + ClearBufferObject("Ethernet0|3-4", CFG_BUFFER_QUEUE_TABLE_NAME); + static_cast(m_dynamicBuffer)->doTask(); + ASSERT_TRUE(m_dynamicBuffer->m_portPgLookup.empty()); + ASSERT_TRUE(m_dynamicBuffer->m_portQueueLookup.empty()); + ASSERT_TRUE(m_dynamicBuffer->m_portProfileListLookups[BUFFER_INGRESS].empty()); + ASSERT_TRUE(m_dynamicBuffer->m_portProfileListLookups[BUFFER_EGRESS].empty()); + ASSERT_TRUE((appBufferPgTable.getKeys(keys), keys.empty())); + ASSERT_TRUE((appBufferQueueTable.getKeys(keys), keys.empty())); + ASSERT_TRUE((appBufferIngProfileListTable.getKeys(keys), keys.empty())); + ASSERT_TRUE((appBufferEgrProfileListTable.getKeys(keys), keys.empty())); + } + } + /* * Port configuration flow * Port table items are received in different order diff --git a/tests/mock_tests/mock_table.cpp b/tests/mock_tests/mock_table.cpp index 0af0cb372f..cd2ffbaa96 100644 --- a/tests/mock_tests/mock_table.cpp +++ b/tests/mock_tests/mock_table.cpp @@ -114,4 +114,12 @@ namespace swss iter->second.swap(new_values); } } + + void ProducerStateTable::del(const std::string &key, + const std::string &op, + const std::string &prefix) + { + auto &table = gDB[m_pipe->getDbId()][getTableName()]; + table.erase(key); + } }