syncd/AsicView.cpp (689 lines of code) (raw):

#include "AsicView.h" #include "VidManager.h" #include "meta/sai_serialize.h" #include "meta/SaiAttributeList.h" #include "swss/logger.h" #include <inttypes.h> #include <algorithm> using namespace syncd; using namespace saimeta; AsicView::AsicView(): m_asicOperationId(0) { SWSS_LOG_ENTER(); m_enableRefernceCountLogs = false; } AsicView::AsicView( _In_ const swss::TableDump &dump): m_asicOperationId(0) { SWSS_LOG_ENTER(); m_enableRefernceCountLogs = false; fromDump(dump); } AsicView::~AsicView() { SWSS_LOG_ENTER(); // empty } /** * @brief Populates ASIC view from REDIS table dump * * @param[in] dump Redis table dump * * NOTE: Could be static method that returns AsicView object. */ void AsicView::fromDump( _In_ const swss::TableDump &dump) { SWSS_LOG_ENTER(); /* * Input should be also existing objects, so they could be created * here right away but we would need VIDs as well. */ int switchesCount = 0; for (const auto &key: dump) { auto start = key.first.find_first_of(":"); if (start == std::string::npos) { SWSS_LOG_THROW("failed to find colon in %s", key.first.c_str()); } std::shared_ptr<SaiObj> o = std::make_shared<SaiObj>(); // TODO we could use sai deserialize object meta key o->m_str_object_type = key.first.substr(0, start); o->m_str_object_id = key.first.substr(start + 1); sai_deserialize_object_type(o->m_str_object_type, o->m_meta_key.objecttype); o->m_info = sai_metadata_get_object_type_info(o->m_meta_key.objecttype); /* * Since neighbor/route/fdb structs objects contains OIDs, we * need to increase vid reference. With new metadata for SAI * 1.0 this can be done in generic way for all non object ids. */ switch (o->m_meta_key.objecttype) { case SAI_OBJECT_TYPE_FDB_ENTRY: sai_deserialize_fdb_entry(o->m_str_object_id, o->m_meta_key.objectkey.key.fdb_entry); m_soFdbs[o->m_str_object_id] = o; break; case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY: sai_deserialize_neighbor_entry(o->m_str_object_id, o->m_meta_key.objectkey.key.neighbor_entry); m_soNeighbors[o->m_str_object_id] = o; m_neighborsByIp[sai_serialize_ip_address(o->m_meta_key.objectkey.key.neighbor_entry.ip_address)].push_back(o->m_str_object_id); break; case SAI_OBJECT_TYPE_ROUTE_ENTRY: sai_deserialize_route_entry(o->m_str_object_id, o->m_meta_key.objectkey.key.route_entry); m_soRoutes[o->m_str_object_id] = o; m_routesByPrefix[sai_serialize_ip_prefix(o->m_meta_key.objectkey.key.route_entry.destination)].push_back(o->m_str_object_id); break; case SAI_OBJECT_TYPE_NAT_ENTRY: sai_deserialize_nat_entry(o->m_str_object_id, o->m_meta_key.objectkey.key.nat_entry); m_soNatEntries[o->m_str_object_id] = o; break; case SAI_OBJECT_TYPE_INSEG_ENTRY: sai_deserialize_inseg_entry(o->m_str_object_id, o->m_meta_key.objectkey.key.inseg_entry); m_soInsegs[o->m_str_object_id] = o; break; default: if (o->m_info->isnonobjectid) { SWSS_LOG_THROW("object %s is non object id, not handled, FIXME", key.first.c_str()); } sai_deserialize_object_id(o->m_str_object_id, o->m_meta_key.objectkey.key.object_id); m_soOids[o->m_str_object_id] = o; m_oOids[o->m_meta_key.objectkey.key.object_id] = o; if (o->m_meta_key.objecttype == SAI_OBJECT_TYPE_SWITCH) { switchesCount++; } break; } m_soAll[o->m_str_object_id] = o; m_sotAll[o->m_meta_key.objecttype][o->m_str_object_id] = o; if (o->m_info->isnonobjectid) { updateNonObjectIdVidReferenceCountByValue(o, 1); } else { /* * Here is only object VID declaration, since we don't * know what objects were processed previously but on * some of previous object attributes this VID could be * used, so value can be already greater than zero, but * here we need to just mark that vid exists in * vidReference. */ m_vidReference[o->m_meta_key.objectkey.key.object_id] += 0; } populateAttributes(o, key.second); } if (switchesCount != 1) { // NOTE: In our solution multiple switches are not supported in single AsicView SWSS_LOG_THROW("only one switch is expected in ASIC view, got: %d switches", switchesCount); } } /** * @brief Release existing VID links (references) based on given attribute. * * @param[in] attr Attribute which will be used to obtain oids. */ void AsicView::releaseExisgingLinks( _In_ const std::shared_ptr<const SaiAttr> &attr) { SWSS_LOG_ENTER(); /* * For each VID on that attribute (it can be single oid or oid list, * release link on current view. * * Second operation after could increase links of setting new attribute, or * nothing if object was removed. * * Also if we want to keep track of object reverse dependency * this action can be more complicated. */ for (auto const &vid: attr->getOidListFromAttribute()) { releaseVidReference(vid); } } /** * @brief Release existing VID links (references) based on given object. * * All OID attributes will be scanned and released. * * @param[in] obj Object which will be used to obtain attributes and oids */ void AsicView::releaseExisgingLinks( _In_ const std::shared_ptr<const SaiObj> &obj) { SWSS_LOG_ENTER(); for (const auto &ita: obj->getAllAttributes()) { releaseExisgingLinks(ita.second); } } /** * @brief Release VID reference. * * If SET operation was performed on attribute, and attribute was OID * attribute, then we need to release previous reference to that VID, * and bind new reference to next OID if present. * * @param[in] vid Virtual ID to be released. */ void AsicView::releaseVidReference( _In_ sai_object_id_t vid) { SWSS_LOG_ENTER(); if (vid == SAI_NULL_OBJECT_ID) { return; } auto it = m_vidReference.find(vid); if (it == m_vidReference.end()) { SWSS_LOG_THROW("vid %s doesn't exist in reference map", sai_serialize_object_id(vid).c_str()); } int referenceCount = --(it->second); if (referenceCount < 0) { SWSS_LOG_THROW("vid %s decreased reference count too many times: %d, BUG", sai_serialize_object_id(vid).c_str(), referenceCount); } SWSS_LOG_INFO("decreased vid %s refrence to %d", sai_serialize_object_id(vid).c_str(), referenceCount); if (referenceCount == 0) { m_vidToAsicOperationId[vid] = m_asicOperationId; } } /** * @brief Bind new links (references) based on attribute * * @param[in] attr Attribute to obtain oids to bind references */ void AsicView::bindNewLinks( _In_ const std::shared_ptr<const SaiAttr> &attr) { SWSS_LOG_ENTER(); /* * For each VID on that attribute (it can be single oid or oid list, * bind new link on current view. * * Notice that this list can contain VIDs from temporary view or default * view, so they may not exists in current view. But that should also be * not the case since we either created new object on current view or * either we matched current object to temporary object so RID can be the * same. * * Also if we want to keep track of object reverse dependency * this action can be more complicated. */ for (auto const &vid: attr->getOidListFromAttribute()) { bindNewVidReference(vid); } } /** * @brief Bind existing VID links (references) based on given object. * * All OID attributes will be scanned and bound. * * @param[in] obj Object which will be used to obtain attributes and oids */ void AsicView::bindNewLinks( _In_ const std::shared_ptr<const SaiObj> &obj) { SWSS_LOG_ENTER(); for (const auto &ita: obj->getAllAttributes()) { bindNewLinks(ita.second); } } /** * @brief Bind new VID reference * * If attribute is OID attribute then we need to increase reference * count on that VID to mark it that it is in use. * * @param[in] vid Virtual ID reference to be bind */ void AsicView::bindNewVidReference( _In_ sai_object_id_t vid) { SWSS_LOG_ENTER(); if (vid == SAI_NULL_OBJECT_ID) { return; } /* * If we are doing bind on new VID reference, that VID needs to * exist int current view, either object was matched or new object * was created. * * TODO: Not sure if this will have impact on some other object * processing since if object was matched or created then this VID * can be found in other attributes comparing them, and it will get * NULL instead RID. */ auto it = m_vidReference.find(vid); if (it == m_vidReference.end()) { SWSS_LOG_THROW("vid %s doesn't exist in reference map", sai_serialize_object_id(vid).c_str()); } int referenceCount = ++(it->second); SWSS_LOG_INFO("increased vid %s reference to %d", sai_serialize_object_id(vid).c_str(), referenceCount); } /** * @brief Gets VID reference count. * * @param vid Virtual ID to obtain reference count. * * @return Reference count or -1 if VID was not found. */ int AsicView::getVidReferenceCount( _In_ sai_object_id_t vid) const { SWSS_LOG_ENTER(); auto it = m_vidReference.find(vid); if (it != m_vidReference.end()) { return it->second; } return -1; } /** * @brief Insert new VID reference. * * Inserts new reference to be tracked. This also make sure that * reference doesn't exist yet, as a sanity check if same reference * would be inserted twice. * * @param[in] vid Virtual ID reference to be inserted. */ void AsicView::insertNewVidReference( _In_ sai_object_id_t vid) { SWSS_LOG_ENTER(); auto it = m_vidReference.find(vid); if (it != m_vidReference.end()) { SWSS_LOG_THROW("vid %s already exist in reference map, BUG", sai_serialize_object_id(vid).c_str()); } m_vidReference[vid] = 0; SWSS_LOG_INFO("inserted vid %s as reference", sai_serialize_object_id(vid).c_str()); } /** * @brief Gets objects by object type. * * @param object_type Object type to be used as filter. * * @return List of objects with requested object type. * Order on list is random. */ std::vector<std::shared_ptr<SaiObj>> AsicView::getObjectsByObjectType( _In_ sai_object_type_t object_type) const { SWSS_LOG_ENTER(); std::vector<std::shared_ptr<SaiObj>> list; /* * We need to use find, since object type may not exist. */ auto it = m_sotAll.find(object_type); if (it == m_sotAll.end()) { return list; } for (const auto &p: it->second) { list.push_back(p.second); } return list; } /** * @brief Gets not processed objects by object type. * * Call to this method can be expensive, since every time we iterate * entire list. This list can contain even 10k elements if view will be * very large. * * @param object_type Object type to be used as filter. * * @return List of objects with requested object type and marked * as not processed. Order on list is random. */ std::vector<std::shared_ptr<SaiObj>> AsicView::getNotProcessedObjectsByObjectType( _In_ sai_object_type_t object_type) const { SWSS_LOG_ENTER(); std::vector<std::shared_ptr<SaiObj>> list; /* * We need to use find, since object type may not exist. */ auto it = m_sotAll.find(object_type); if (it == m_sotAll.end()) { return list; } for (const auto &p: it->second) { if (p.second->getObjectStatus() == SAI_OBJECT_STATUS_NOT_PROCESSED) { list.push_back(p.second); } } return list; } /** * @brief Gets all not processed objects * * @return List of all not processed objects. Order on list is random. */ std::vector<std::shared_ptr<SaiObj>> AsicView::getAllNotProcessedObjects() const { SWSS_LOG_ENTER(); std::vector<std::shared_ptr<SaiObj>> list; for (const auto &p: m_soAll) { if (p.second->getObjectStatus() == SAI_OBJECT_STATUS_NOT_PROCESSED) { list.push_back(p.second); } } return list; } /** * @brief Create dummy existing object * * Function creates dummy object, which is used to indicate that * this OID object exist in current view. This is used for existing * objects, like CPU port, default trap group. * * @param[in] rid Real ID * @param[in] vid Virtual ID */ std::shared_ptr<SaiObj> AsicView::createDummyExistingObject( _In_ sai_object_id_t rid, _In_ sai_object_id_t vid) { SWSS_LOG_ENTER(); sai_object_type_t object_type = VidManager::objectTypeQuery(vid); if (object_type == SAI_OBJECT_TYPE_NULL) { SWSS_LOG_THROW("got null object type from VID %s", sai_serialize_object_id(vid).c_str()); } std::shared_ptr<SaiObj> o = std::make_shared<SaiObj>(); o->m_str_object_type = sai_serialize_object_type(object_type); o->m_str_object_id = sai_serialize_object_id(vid); o->m_meta_key.objecttype = object_type; o->m_meta_key.objectkey.key.object_id = vid; o->m_info = sai_metadata_get_object_type_info(object_type); m_soOids[o->m_str_object_id] = o; m_oOids[vid] = o; m_vidReference[vid] += 0; m_soAll[o->m_str_object_id] = o; m_sotAll[o->m_meta_key.objecttype][o->m_str_object_id] = o; m_ridToVid[rid] = vid; m_vidToRid[vid] = rid; return o; } /** * @brief Generate ASIC set operation on current existing object. * * NOTE: In long run, this is serialize, and then we call deserialize * to execute them on actual ASIC, maybe this is not necessary * and could be optimized later. * * TODO: Set on object id should do release of links (currently done * outside) and modify dependency tree. * * @param currentObj Current object. * @param attr Attribute to be set on current object. */ void AsicView::asicSetAttribute( _In_ const std::shared_ptr<SaiObj> &currentObj, _In_ const std::shared_ptr<SaiAttr> &attr) { SWSS_LOG_ENTER(); SWSS_LOG_INFO("%s: %s -> %s:%s", currentObj->m_str_object_type.c_str(), currentObj->m_str_object_id.c_str(), attr->getStrAttrId().c_str(), attr->getStrAttrValue().c_str()); m_asicOperationId++; /* * Release previous references if attribute is object id and bind * new reference in that place. */ auto meta = attr->getAttrMetadata(); auto currentAttr = currentObj->tryGetSaiAttr(meta->attrid); if (attr->isObjectIdAttr()) { if (currentObj->hasAttr(meta->attrid)) { /* * Since previous attribute exists, lets release previous links if * they are not NULL. */ releaseExisgingLinks(currentObj->getSaiAttr(meta->attrid)); } currentObj->setAttr(attr); bindNewLinks(currentObj->getSaiAttr(meta->attrid)); } else { /* * This SET don't contain any OIDs so no extra operations are required, * we don't need to break any references and decrease any * reference count. * * Also this attribute may exist already on this object or it will be * just set now, so just in case lets make copy of it. * * Making copy here is not necessary since default attribute will be * created dynamically anyway, and temporary attributes will not change * also. */ currentObj->setAttr(attr); } auto entry = SaiAttributeList::serialize_attr_list( currentObj->getObjectType(), 1, attr->getSaiAttr(), false); std::string key = currentObj->m_str_object_type + ":" + currentObj->m_str_object_id; auto kco = std::make_shared<swss::KeyOpFieldsValuesTuple>(key, "set", entry); sai_object_id_t vid = (currentObj->isOidObject()) ? currentObj->getVid() : SAI_NULL_OBJECT_ID; m_asicOperations.push_back(AsicOperation(m_asicOperationId, vid, false, kco)); if (currentAttr) { // if current attribute exists, save it value for log purpose m_asicOperations.rbegin()->m_currentValue = currentAttr->getStrAttrValue(); } dumpRef("set"); } /** * @brief Generate ASIC create operation for current object. * * NOTE: In long run, this is serialize, and then we call * deserialize to execute them on actual asic, maybe this is not * necessary and could be optimized later. * * TODO: Create on object id attributes should bind references to * used VIDs of of links (currently done outside) and modify * dependency tree. * * @param currentObject Current object to be created. */ void AsicView::asicCreateObject( _In_ const std::shared_ptr<SaiObj> &currentObj) { SWSS_LOG_ENTER(); SWSS_LOG_INFO("%s: %s", currentObj->m_str_object_type.c_str(), currentObj->m_str_object_id.c_str()); m_asicOperationId++; if (currentObj->isOidObject()) { m_soOids[currentObj->m_str_object_id] = currentObj; m_oOids[currentObj->m_meta_key.objectkey.key.object_id] = currentObj; m_soAll[currentObj->m_str_object_id] = currentObj; m_sotAll[currentObj->m_meta_key.objecttype][currentObj->m_str_object_id] = currentObj; /* * Since we are creating object, we just need to mark that * reference is there. But at this point object is not used * anywhere. */ m_vidReference[currentObj->m_meta_key.objectkey.key.object_id] += 0; } else { /* * Since neighbor/route/fdb structs objects contains OIDs, we * need to increase vid reference. With new metadata for SAI * 1.0 this can be done in generic way for all non object ids. */ // TODO why we are doing deserialize here? meta key should be populated already switch (currentObj->getObjectType()) { case SAI_OBJECT_TYPE_FDB_ENTRY: //sai_deserialize_fdb_entry(currentObj->m_str_object_id, currentObj->m_meta_key.objectkey.key.fdb_entry); m_soFdbs[currentObj->m_str_object_id] = currentObj; break; case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY: //sai_deserialize_neighbor_entry(currentObj->m_str_object_id, currentObj->m_meta_key.objectkey.key.neighbor_entry); m_soNeighbors[currentObj->m_str_object_id] = currentObj; break; case SAI_OBJECT_TYPE_ROUTE_ENTRY: //sai_deserialize_route_entry(currentObj->m_str_object_id, currentObj->m_meta_key.objectkey.key.route_entry); m_soRoutes[currentObj->m_str_object_id] = currentObj; break; case SAI_OBJECT_TYPE_NAT_ENTRY: m_soNatEntries[currentObj->m_str_object_id] = currentObj; break; case SAI_OBJECT_TYPE_INSEG_ENTRY: m_soInsegs[currentObj->m_str_object_id] = currentObj; break; default: SWSS_LOG_THROW("unsupported object type: %s", sai_serialize_object_type(currentObj->getObjectType()).c_str()); } m_soAll[currentObj->m_str_object_id] = currentObj; m_sotAll[currentObj->m_meta_key.objecttype][currentObj->m_str_object_id] = currentObj; updateNonObjectIdVidReferenceCountByValue(currentObj, 1); } bindNewLinks(currentObj); // handle attribute references /* * Generate asic commands. */ std::vector<swss::FieldValueTuple> entry; for (auto const &pair: currentObj->getAllAttributes()) { const auto &attr = pair.second; swss::FieldValueTuple fvt(attr->getStrAttrId(), attr->getStrAttrValue()); entry.push_back(fvt); } if (entry.size() == 0) { /* * Make sure that we put object into db even if there are no * attributes set. */ swss::FieldValueTuple null("NULL", "NULL"); entry.push_back(null); } std::string key = currentObj->m_str_object_type + ":" + currentObj->m_str_object_id; auto kco = std::make_shared<swss::KeyOpFieldsValuesTuple>(key, "create", entry); sai_object_id_t vid = (currentObj->isOidObject()) ? currentObj->getVid() : SAI_NULL_OBJECT_ID; m_asicOperations.push_back(AsicOperation(m_asicOperationId, vid, false, kco)); dumpRef("create"); } /** * @brief Generate ASIC remove operation for current existing object. * * @param currentObj Current existing object to be removed. */ void AsicView::asicRemoveObject( _In_ const std::shared_ptr<SaiObj> &currentObj) { SWSS_LOG_ENTER(); SWSS_LOG_INFO("%s: %s", currentObj->m_str_object_type.c_str(), currentObj->m_str_object_id.c_str()); if (currentObj->getObjectStatus() != SAI_OBJECT_STATUS_NOT_PROCESSED) { SWSS_LOG_THROW("FATAL: removing object with status: %d, logic error", currentObj->getObjectStatus()); } m_asicOperationId++; if (currentObj->isOidObject()) { /* * Reference count is already check externally, but we can move * that check here also as sanity check. */ int count = getVidReferenceCount(currentObj->getVid()); if (count != 0) { SWSS_LOG_THROW("can't remove existing object %s:%s since reference count is %d, FIXME", currentObj->m_str_object_type.c_str(), currentObj->m_str_object_id.c_str(), count); } m_soOids.erase(currentObj->m_str_object_id); m_oOids.erase(currentObj->m_meta_key.objectkey.key.object_id); m_soAll.erase(currentObj->m_str_object_id); m_sotAll.at(currentObj->m_meta_key.objecttype).erase(currentObj->m_str_object_id); m_vidReference[currentObj->m_meta_key.objectkey.key.object_id] -= 1; /* * Clear object also from rid/vid maps. */ sai_object_id_t vid = currentObj->getVid(); sai_object_id_t rid = m_vidToRid.at(vid); /* * This will have impact on translate_vid_to_rid, we need to put * this in other view. */ m_ridToVid.erase(rid); m_vidToRid.erase(vid); /* * We could remove this VID also from m_vidReference, but it's not * required. */ m_removedVidToRid[vid] = rid; } else { /* * Since neighbor/route/fdb structs objects contains OIDs, we * need to decrease VID reference. With new metadata for SAI * 1.0 this can be done in generic way for all non object ids. */ switch (currentObj->getObjectType()) { case SAI_OBJECT_TYPE_FDB_ENTRY: m_soFdbs.erase(currentObj->m_str_object_id); break; case SAI_OBJECT_TYPE_NEIGHBOR_ENTRY: m_soNeighbors.erase(currentObj->m_str_object_id); break; case SAI_OBJECT_TYPE_ROUTE_ENTRY: m_soRoutes.erase(currentObj->m_str_object_id); break; case SAI_OBJECT_TYPE_NAT_ENTRY: m_soNatEntries.erase(currentObj->m_str_object_id); break; case SAI_OBJECT_TYPE_INSEG_ENTRY: m_soInsegs.erase(currentObj->m_str_object_id); break; default: SWSS_LOG_THROW("unsupported object type: %s", sai_serialize_object_type(currentObj->getObjectType()).c_str()); } m_soAll.erase(currentObj->m_str_object_id); m_sotAll.at(currentObj->m_meta_key.objecttype).erase(currentObj->m_str_object_id); updateNonObjectIdVidReferenceCountByValue(currentObj, -1); } releaseExisgingLinks(currentObj); // handle attribute references /* * Generate asic commands. */ std::vector<swss::FieldValueTuple> entry; std::string key = currentObj->m_str_object_type + ":" + currentObj->m_str_object_id; auto kco = std::make_shared<swss::KeyOpFieldsValuesTuple>(key, "remove", entry); sai_object_id_t vid = (currentObj->isOidObject()) ? currentObj->getVid() : SAI_NULL_OBJECT_ID; if (currentObj->isOidObject()) { m_asicOperations.push_back(AsicOperation(m_asicOperationId, vid, true, kco)); } else { /* * When doing remove of non object id, let's put it on front, * since when removing next hop group from group last member, * and group is in use by some route, then remove fails since * group cant be empty. * * Of course this doesn't guarantee that remove all routes will * be at the beginning, also it may happen that default route * will be removed first which maybe not allowed. */ m_asicRemoveOperationsNonObjectId.push_back(AsicOperation(m_asicOperationId, vid, true, kco)); } dumpRef("remove"); } std::vector<AsicOperation> AsicView::asicGetWithOptimizedRemoveOperations() const { SWSS_LOG_ENTER(); SWSS_LOG_TIMER("optimizing asic remove operations"); std::vector<AsicOperation> v; /* * First push remove operations on non object id at the beginning of list. */ for (const auto &n: m_asicRemoveOperationsNonObjectId) { v.push_back(n); } size_t index = v.size(); size_t moved = 0; for (auto opit = m_asicOperations.begin(); opit != m_asicOperations.end(); ++opit) { const auto &op = *opit; if (!op.m_isRemove) { /* * This is create or set operation, put it at the list end. */ v.push_back(op); continue; } /* * NOTE: When operation is SET on OID object it can release * references on that OID, and if that OID is later to be * removed a wrong order of operations will happen: * * not optimized scenario: * - create object A * - set object B attr (releases reference on C) * - remove object D (release reference on C) * - remove object C * * this logic could result with wrong order: * - remove D * - remove C (will break reference count on C, since used in B) * since D is considered last OP releasing C reference * - create A * - set object B with A * * correct optimized logic: * - remove D * - create A * - set object B with A * - remove C * * This is fixed by checking if last operation to release reference was REMOVE */ if (op.m_vid == SAI_NULL_OBJECT_ID) { SWSS_LOG_THROW("non object id remove not expected here"); } auto mit = m_vidToAsicOperationId.find(op.m_vid); if (mit == m_vidToAsicOperationId.end()) { /* * This vid is not present in map, so we can move remove * operation all the way to the top since there is no * operation that decreased this vid reference. * * This can be NHG member, vlan member etc. */ v.insert(v.begin() + index, op); auto ot = VidManager::objectTypeQuery(op.m_vid); SWSS_LOG_INFO("move %s all way up (not in map): %s to index: %zu", sai_serialize_object_id(op.m_vid).c_str(), sai_serialize_object_type(ot).c_str(), index); index++; moved++; continue; } /* * This last operation id that decreased VID reference to zero * can be before or after current iterator, so it may be not * found on list from current iterator to list end. This will * mean that we can insert this remove at current iterator * position. * * If operation is found then we need to insert this op after * current iterator and forward iterator. * * But there is a catch here, if that last operation was REMOVE, * then it was forwarded all the way to the UP, but in the * middle we could have some "SET" operations that would * release this reference also we in this case, we can't just * move this remove just after previous remove. */ int lastOpIdDecRef = mit->second; auto itr = find_if(v.begin(), v.end(), [lastOpIdDecRef] (const AsicOperation& ao) { return ao.m_opId == lastOpIdDecRef; } ); if (itr == v.end()) { SWSS_LOG_THROW("something wrong, vid %s in map, but not found on list!", sai_serialize_object_id(op.m_vid).c_str()); } if (itr->m_isRemove) { SWSS_LOG_INFO("previous operation to release reference %s was also REMOVE, we push current REMOVE op at the list end", sai_serialize_object_id(mit->first).c_str()); /* * If previous operation was REMOVE (it could be pushed to * the top) so push current operation to the list here, * instead of possible break reference count on SET * operations. */ v.push_back(op); continue; } /* * We add +1 since we need to insert current object AFTER the one that we found */ size_t lastOpIdDecRefIndex = itr - v.begin() + 1; if (lastOpIdDecRefIndex > index) { SWSS_LOG_INFO("index update from %zu to %zu", index, lastOpIdDecRefIndex); index = lastOpIdDecRefIndex; } v.insert(v.begin() + index, op); auto ot = VidManager::objectTypeQuery(op.m_vid); SWSS_LOG_INFO("move 0x%" PRIx64 " in the middle up: %s (last: %zu curr: %zu)", op.m_vid, sai_serialize_object_type(ot).c_str(), lastOpIdDecRefIndex, index); index++; moved++; } SWSS_LOG_NOTICE("moved %zu REMOVE operations upper in stack from total %zu operations", moved, v.size()); return v; } std::vector<AsicOperation> AsicView::asicGetOperations() const { SWSS_LOG_ENTER(); // XXX it will copy entire vector :/, expensive, but // in most cases we will have very small number operations to execute /* * We need to put remove operations of non object id first because * of removing last next hop group member if group is in use. */ auto sum = m_asicRemoveOperationsNonObjectId; sum.insert(sum.end(), m_asicOperations.begin(), m_asicOperations.end()); return sum; } size_t AsicView::asicGetOperationsCount() const { SWSS_LOG_ENTER(); return m_asicOperations.size() + m_asicRemoveOperationsNonObjectId.size(); } bool AsicView::hasRid( _In_ sai_object_id_t rid) const { SWSS_LOG_ENTER(); return m_ridToVid.find(rid) != m_ridToVid.end(); } bool AsicView::hasVid( _In_ sai_object_id_t vid) const { SWSS_LOG_ENTER(); return m_vidToRid.find(vid) != m_vidToRid.end(); } void AsicView::dumpRef(const std::string & asicName) { SWSS_LOG_ENTER(); if (m_enableRefernceCountLogs == false) return; SWSS_LOG_NOTICE("dump references in ASIC VIEW: %s", asicName.c_str()); for (auto& kvp: m_vidReference) { sai_object_id_t oid = kvp.first; auto ot = VidManager::objectTypeQuery(oid); switch (ot) { case SAI_OBJECT_TYPE_LAG: case SAI_OBJECT_TYPE_NEXT_HOP: case SAI_OBJECT_TYPE_NEXT_HOP_GROUP: case SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER: case SAI_OBJECT_TYPE_ROUTE_ENTRY: case SAI_OBJECT_TYPE_ROUTER_INTERFACE: break; // skip object we have no interest into default: continue; } SWSS_LOG_NOTICE("ref %s: %s: %d", sai_serialize_object_type(ot).c_str(), sai_serialize_object_id(oid).c_str(), kvp.second); } } void AsicView::dumpVidToAsicOperatioId() const { SWSS_LOG_ENTER(); for (auto& a: m_vidToAsicOperationId) { auto ot = VidManager::objectTypeQuery(a.first); SWSS_LOG_WARN("%d: %s:%s", a.second, sai_serialize_object_type(ot).c_str(), sai_serialize_object_id(a.first).c_str()); } } void AsicView::populateAttributes( _In_ std::shared_ptr<SaiObj> &obj, _In_ const swss::TableMap &map) { SWSS_LOG_ENTER(); for (const auto& field: map) { std::shared_ptr<SaiAttr> attr = std::make_shared<SaiAttr>(field.first, field.second); if (obj->getObjectType() == SAI_OBJECT_TYPE_ACL_COUNTER) { auto* meta = attr->getAttrMetadata(); switch (meta->attrid) { case SAI_ACL_COUNTER_ATTR_PACKETS: case SAI_ACL_COUNTER_ATTR_BYTES: // when reading asic view, ignore acl counter packets and bytes // this will result to not compare them during comparison logic SWSS_LOG_INFO("ignoring %s for %s", meta->attridname, obj->m_str_object_id.c_str()); continue; default: break; } } if (obj->getObjectType() == SAI_OBJECT_TYPE_NAT_ENTRY) { auto* meta = attr->getAttrMetadata(); switch (meta->attrid) { case SAI_NAT_ENTRY_ATTR_HIT_BIT_COR: case SAI_NAT_ENTRY_ATTR_HIT_BIT: // when reading asic view, ignore Nat entry hit-bit attribute // this will result to not compare them during comparison logic SWSS_LOG_INFO("ignoring %s for %s", meta->attridname, obj->m_str_object_id.c_str()); continue; default: break; } } obj->setAttr(attr); /* * Since attributes can contain OIDs we need to update * reference count on them. */ for (auto const &vid: attr->getOidListFromAttribute()) { if (vid != SAI_NULL_OBJECT_ID) { m_vidReference[vid] += 1; } } } } /** * @brief Update non object id VID reference count by specified value. * * Method will iterate via all OID struct members in non object id and * update reference count by specified value. * * @param currentObj Current object to be processed. * @param value Value by which reference will be updated. Can be negative. */ void AsicView::updateNonObjectIdVidReferenceCountByValue( _In_ const std::shared_ptr<SaiObj> &currentObj, _In_ int value) { SWSS_LOG_ENTER(); for (size_t j = 0; j < currentObj->m_info->structmemberscount; ++j) { const sai_struct_member_info_t *m = currentObj->m_info->structmembers[j]; if (m->membervaluetype == SAI_ATTR_VALUE_TYPE_OBJECT_ID) { sai_object_id_t vid = m->getoid(&currentObj->m_meta_key); m_vidReference[vid] += value; if (m_enableRefernceCountLogs) { SWSS_LOG_WARN("updated vid %s reference to %d", sai_serialize_object_id(vid).c_str(), m_vidReference[vid]); } if (m_vidReference[vid] == 0) { m_vidToAsicOperationId[vid] = m_asicOperationId; } } } } void AsicView::checkObjectsStatus() const { SWSS_LOG_ENTER(); int count = 0; for (const auto &p: m_soAll) { if (p.second->getObjectStatus() != SAI_OBJECT_STATUS_FINAL) { const auto &o = *p.second; SWSS_LOG_ERROR("object was not processed: %s %s, status: %s (ref: %d)", o.m_str_object_type.c_str(), o.m_str_object_id.c_str(), ObjectStatus::sai_serialize_object_status(o.getObjectStatus()).c_str(), o.isOidObject() ? getVidReferenceCount(o.getVid()): -1); count++; } } if (count > 0) { SWSS_LOG_THROW("%d objects were not processed", count); } } sai_object_id_t AsicView::getSwitchVid() const { SWSS_LOG_ENTER(); for (auto& kvp: m_vidToRid) { auto vid = kvp.first; if (VidManager::objectTypeQuery(vid) == SAI_OBJECT_TYPE_SWITCH) { return vid; } } SWSS_LOG_THROW("no SWITCH present in ASIC view, FATAL"); }