-
Notifications
You must be signed in to change notification settings - Fork 694
[fdborch]: Flush fdb entries on port link down (Resolves #1063, #1294, resolves Azure/sonic-buildimage#4184) #1295
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -186,17 +186,95 @@ void FdbOrch::update(sai_fdb_event_t type, const sai_fdb_entry_t* entry, sai_obj | |
| } | ||
| else if (bridge_port_id && entry->bv_id == SAI_NULL_OBJECT_ID) | ||
| { | ||
| /*this is a placeholder for flush port fdb case, not supported yet.*/ | ||
| SWSS_LOG_ERROR("FdbOrch notification: not supported flush port fdb action, port_id = 0x%" PRIx64 ", bv_id = 0x%" PRIx64 ".", bridge_port_id, entry->bv_id); | ||
| if (!m_portsOrch->getPortByBridgePortId(bridge_port_id, update.port)) | ||
| { | ||
| SWSS_LOG_ERROR("Failed to get port by bridge port ID 0x%" PRIx64, bridge_port_id); | ||
| return; | ||
| } | ||
|
|
||
| update.entry.port_name = update.port.m_alias; | ||
|
|
||
| /* Remove all FDB entries of specified port by going through m_entries */ | ||
| for (auto itr = m_entries.begin(); itr != m_entries.end();) | ||
| { | ||
| if (itr->port_name == update.port.m_alias) | ||
| { | ||
| update.entry.mac = itr->mac; | ||
| update.entry.bv_id = itr->bv_id; | ||
| update.add = false; | ||
| itr++; | ||
|
|
||
| storeFdbEntryState(update); | ||
|
|
||
| SWSS_LOG_DEBUG("FdbOrch notification: mac %s , bv_id 0x%" PRIx64 " on port ID 0x%" PRIx64 " was removed", update.entry.mac.to_string().c_str(), update.entry.bv_id, bridge_port_id); | ||
|
|
||
| for (auto observer: m_observers) | ||
| { | ||
| observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); | ||
| } | ||
| continue; | ||
| } | ||
| itr++; | ||
| } | ||
| } | ||
| else if (bridge_port_id == SAI_NULL_OBJECT_ID && entry->bv_id != SAI_NULL_OBJECT_ID) | ||
| { | ||
| /*this is a placeholder for flush vlan fdb case, not supported yet.*/ | ||
| SWSS_LOG_ERROR("FdbOrch notification: not supported flush vlan fdb action, port_id = 0x%" PRIx64 ", bv_id = 0x%" PRIx64 ".", bridge_port_id, entry->bv_id); | ||
| /* Remove all FDB entries of specified vlan by going through m_entries */ | ||
| for (auto itr = m_entries.begin(); itr != m_entries.end();) | ||
| { | ||
| if (itr->bv_id == entry->bv_id) | ||
| { | ||
| update.entry.mac = itr->mac; | ||
| update.entry.bv_id = itr->bv_id; | ||
| update.add = false; | ||
| itr++; | ||
|
|
||
| storeFdbEntryState(update); | ||
|
|
||
| SWSS_LOG_DEBUG("FdbOrch notification: mac %s , bv_id 0x%" PRIx64 " was removed", update.entry.mac.to_string().c_str(), update.entry.bv_id); | ||
|
|
||
| for (auto observer: m_observers) | ||
| { | ||
| observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); | ||
| } | ||
| continue; | ||
| } | ||
| itr++; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| SWSS_LOG_ERROR("FdbOrch notification: not supported flush fdb action, port_id = 0x%" PRIx64 ", bv_id = 0x%" PRIx64 ".", bridge_port_id, entry->bv_id); | ||
| if (!m_portsOrch->getPortByBridgePortId(bridge_port_id, update.port)) | ||
| { | ||
| SWSS_LOG_ERROR("Failed to get port by bridge port ID 0x%" PRIx64, bridge_port_id); | ||
| return; | ||
| } | ||
|
|
||
| update.entry.port_name = update.port.m_alias; | ||
|
|
||
| /* Remove all FDB entries of specified port and vlan by going through m_entries */ | ||
| for (auto itr = m_entries.begin(); itr != m_entries.end();) | ||
| { | ||
| if ((itr->port_name == update.port.m_alias) | ||
| && (itr->bv_id == entry->bv_id)) | ||
| { | ||
| update.entry.mac = itr->mac; | ||
| update.entry.bv_id = itr->bv_id; | ||
| update.add = false; | ||
| itr++; | ||
|
|
||
| storeFdbEntryState(update); | ||
|
|
||
| SWSS_LOG_DEBUG("FdbOrch notification: mac %s , bv_id 0x%" PRIx64 " on port ID 0x%" PRIx64 " was removed", update.entry.mac.to_string().c_str(), update.entry.bv_id, bridge_port_id); | ||
|
|
||
| for (auto observer: m_observers) | ||
| { | ||
| observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); | ||
| } | ||
| continue; | ||
| } | ||
| itr++; | ||
| } | ||
| } | ||
| break; | ||
| } | ||
|
|
@@ -259,6 +337,40 @@ bool FdbOrch::getPort(const MacAddress& mac, uint16_t vlan, Port& port) | |
| return true; | ||
| } | ||
|
|
||
| bool FdbOrch::ifChangeInformFdb(Port &port, bool if_up) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the name looks better as |
||
| { | ||
| SWSS_LOG_ENTER(); | ||
|
|
||
| sai_attribute_t attr; | ||
| vector<sai_attribute_t> attrs; | ||
|
|
||
| if (if_up) | ||
| { | ||
| SWSS_LOG_DEBUG("Port %s is goning up, and no need updating FDB for this port.", port.m_alias.c_str()); | ||
| return true; | ||
| } | ||
|
|
||
| attr.id = SAI_FDB_FLUSH_ATTR_ENTRY_TYPE; | ||
| attr.value.s32 = SAI_FDB_ENTRY_TYPE_DYNAMIC; | ||
| attrs.push_back(attr); | ||
|
|
||
| attr.id = SAI_FDB_FLUSH_ATTR_BRIDGE_PORT_ID; | ||
| attr.value.oid = port.m_bridge_port_id; | ||
| attrs.push_back(attr); | ||
|
|
||
| sai_status_t status = sai_fdb_api->flush_fdb_entries(gSwitchId, (uint32_t)attrs.size(), attrs.data()); | ||
| if (status != SAI_STATUS_SUCCESS) | ||
| { | ||
| SWSS_LOG_ERROR("Failed to flush dynamic FDB on %s, rv:%d", | ||
| port.m_alias.c_str(), status); | ||
| return false; | ||
| } | ||
|
|
||
| SWSS_LOG_NOTICE("Success to flush dynamic FDB on %s", port.m_alias.c_str()); | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| void FdbOrch::doTask(Consumer& consumer) | ||
| { | ||
| SWSS_LOG_ENTER(); | ||
|
|
@@ -310,6 +422,8 @@ void FdbOrch::doTask(Consumer& consumer) | |
| /* FDB type is either dynamic or static */ | ||
| assert(type == "dynamic" || type == "static"); | ||
|
|
||
| entry.port_name = port; | ||
|
|
||
| if (addFdbEntry(entry, port, type)) | ||
| it = consumer.m_toSync.erase(it); | ||
| else | ||
|
|
@@ -429,7 +543,7 @@ void FdbOrch::updateVlanMember(const VlanMemberUpdate& update) | |
| auto fdb_list = std::move(saved_fdb_entries[port_name]); | ||
| if(!fdb_list.empty()) | ||
| { | ||
| for (const auto& fdb: fdb_list) | ||
| for (auto& fdb: fdb_list) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can avoid making the mutable. |
||
| { | ||
| // try to insert an FDB entry. If the FDB entry is not ready to be inserted yet, | ||
| // it would be added back to the saved_fdb_entries structure by addFDBEntry() | ||
|
|
@@ -438,7 +552,7 @@ void FdbOrch::updateVlanMember(const VlanMemberUpdate& update) | |
| } | ||
| } | ||
|
|
||
| bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const string& type) | ||
| bool FdbOrch::addFdbEntry(FdbEntry& entry, const string& port_name, const string& type) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, I think, we shouldn't make entry mutable. Here is my suggestion: Change prototype of addFdbEntry() by removing port from parameter list, as it's already part of FDB entry structure. As far as I remember addFdbEntry is called from only two places and you can populate entry.port_name in both places. In fact you don't have to do anything when it's called from updateVlanMember(). Also note that you have to make some changes in storeFdbStateEntry(). LEARN is the only case where storeFdbStateEntry() ends up adding the entry to m_entries/saved_fdb_entries. So, populate entry.port_name in LEARN case before calling storeFdbStateEntry(). And in the other two cases(AGED/MOVE, FLUSH), anyways we are going to remove the entry from m_entries, hence no need to populate entry.port_name. Also note that you can go ahead and cleanup(delete) few lines of port related code in storeFdbStateEntry(). |
||
| { | ||
| SWSS_LOG_ENTER(); | ||
|
|
||
|
|
@@ -467,6 +581,12 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const | |
| return true; | ||
| } | ||
|
|
||
| /* Update entry's port name if needed */ | ||
| if (entry.port_name.empty()) | ||
| { | ||
| entry.port_name = port_name; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In which case, the entry is created without port name?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We add an entry to m_entries in two places. One is storeFdbEntryState() and other is addFdbEntry(), Could you please make sure in both places we set port_name in entry before adding to m_entries?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, this part of codes is now redundant in my PR. |
||
| } | ||
|
|
||
| sai_attribute_t attr; | ||
| vector<sai_attribute_t> attrs; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ struct FdbEntry | |
| { | ||
| MacAddress mac; | ||
| sai_object_id_t bv_id; | ||
| std::string port_name; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I liked this. This was much needed! |
||
|
|
||
| bool operator<(const FdbEntry& other) const | ||
| { | ||
|
|
@@ -46,6 +47,7 @@ class FdbOrch: public Orch, public Subject, public Observer | |
| void update(sai_fdb_event_t, const sai_fdb_entry_t *, sai_object_id_t); | ||
| void update(SubjectType type, void *cntx); | ||
| bool getPort(const MacAddress&, uint16_t, Port&); | ||
| bool ifChangeInformFdb(Port&, bool); | ||
|
|
||
| private: | ||
| PortsOrch *m_portsOrch; | ||
|
|
@@ -60,7 +62,7 @@ class FdbOrch: public Orch, public Subject, public Observer | |
| void doTask(NotificationConsumer& consumer); | ||
|
|
||
| void updateVlanMember(const VlanMemberUpdate&); | ||
| bool addFdbEntry(const FdbEntry&, const string&, const string&); | ||
| bool addFdbEntry(FdbEntry&, const string&, const string&); | ||
| bool removeFdbEntry(const FdbEntry&); | ||
|
|
||
| bool storeFdbEntryState(const FdbUpdate& update); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
| #include "intfsorch.h" | ||
| #include "bufferorch.h" | ||
| #include "neighorch.h" | ||
| #include "fdborch.h" | ||
|
|
||
| #include <inttypes.h> | ||
| #include <cassert> | ||
|
|
@@ -39,6 +40,7 @@ extern IntfsOrch *gIntfsOrch; | |
| extern NeighOrch *gNeighOrch; | ||
| extern CrmOrch *gCrmOrch; | ||
| extern BufferOrch *gBufferOrch; | ||
| extern FdbOrch *gFdbOrch; | ||
|
|
||
| #define VLAN_PREFIX "Vlan" | ||
| #define DEFAULT_VLAN_ID 1 | ||
|
|
@@ -3830,6 +3832,11 @@ void PortsOrch::updatePortOperStatus(Port &port, sai_port_oper_status_t status) | |
| { | ||
| SWSS_LOG_WARN("Inform nexthop operation failed for interface %s", port.m_alias.c_str()); | ||
| } | ||
|
|
||
| if (!gFdbOrch->ifChangeInformFdb(port, isUp)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that we are just following the way ifChangeInformNextHop is done and introducing ifChangeInformFdb. And I was coding up the same solution yesterday and thought of doing it a different way. That is, today at most places if NOT all, we interact between modules using observer pattern. For example, when port gets created or deleted, portsOrch notifies all the observers. I think we should leverage that or follow the pattern to notify observers about port state change. That way we dont have introduce a new function for every module that wants to port status change. With my new code change, we should be able to delete ifChangeInformNextHop and let neighborOrch handle port status change notification. Please refer PR #1242. I will push the code in couple of hours |
||
| { | ||
| SWSS_LOG_WARN("Inform fdb operation failed for interface %s", port.m_alias.c_str()); | ||
| } | ||
| } | ||
|
|
||
| /* | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When we call SAI flush API, with bridge_port_id, we get SAI_FDB_EVENT_AGED event but NOT SAI_FDB_EVENT_FLUSHED. I did verify it by looking at logs. So, here is my understanding
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I take back my comment on receiving SAI_FDB_EVENT_AGED instead of instead of SAI_FDB_EVENT_FLUSHED. My observation was on one platform. But it was NOT the same on VS container