diff options
Diffstat (limited to 'include/ibm/locks.hpp')
-rw-r--r-- | include/ibm/locks.hpp | 610 |
1 files changed, 0 insertions, 610 deletions
diff --git a/include/ibm/locks.hpp b/include/ibm/locks.hpp deleted file mode 100644 index 3bb82b9479..0000000000 --- a/include/ibm/locks.hpp +++ /dev/null @@ -1,610 +0,0 @@ -#pragma once - -#include "ibm/utils.hpp" -#include "logging.hpp" - -#include <boost/container/flat_map.hpp> -#include <boost/endian/conversion.hpp> -#include <nlohmann/json.hpp> - -#include <filesystem> -#include <fstream> -#include <variant> - -namespace crow -{ -namespace ibm_mc_lock -{ - -using SType = std::string; - -/*---------------------------------------- -|Segment flags : LockFlag | SegmentLength| -------------------------------------------*/ - -using SegmentFlags = std::vector<std::pair<SType, uint32_t>>; - -// Lockrequest = session-id | hmc-id | locktype | resourceid | segmentinfo -using LockRequest = std::tuple<SType, SType, SType, uint64_t, SegmentFlags>; -using LockRequests = std::vector<LockRequest>; -using Rc = - std::pair<bool, std::variant<uint32_t, std::pair<uint32_t, LockRequest>>>; -using RcRelaseLock = std::pair<bool, std::pair<uint32_t, LockRequest>>; -using RcGetLockList = - std::variant<std::string, std::vector<std::pair<uint32_t, LockRequests>>>; -using ListOfTransactionIds = std::vector<uint32_t>; -using RcAcquireLock = std::pair<bool, std::variant<Rc, std::pair<bool, int>>>; -using RcReleaseLockApi = std::pair<bool, std::variant<bool, RcRelaseLock>>; -using SessionFlags = std::pair<SType, SType>; -using ListOfSessionIds = std::vector<std::string>; - -class Lock -{ - uint32_t transactionId = 0; - boost::container::flat_map<uint32_t, LockRequests> lockTable; - - protected: - /* - * This function implements the logic for validating an incoming - * lock request/requests. - * - * Returns : True (if Valid) - * Returns : False (if not a Valid lock request) - */ - - virtual bool isValidLockRequest(const LockRequest& refLockRecord); - - /* - * This function implements the logic of checking if the incoming - * multi-lock request is not having conflicting requirements. - * - * Returns : True (if conflicting) - * Returns : False (if not conflicting) - */ - - virtual bool isConflictRequest(const LockRequests& refLockRequestStructure); - /* - * Implements the core algorithm to find the conflicting - * lock requests. - * - * This functions takes two lock requests and check if both - * are conflicting to each other. - * - * Returns : True (if conflicting) - * Returns : False (if not conflicting) - */ - virtual bool isConflictRecord(const LockRequest& refLockRecord1, - const LockRequest& refLockRecord2); - - /* - * This function implements the logic of checking the conflicting - * locks from a incoming single/multi lock requests with the already - * existing lock request in the lock table. - * - */ - - virtual Rc isConflictWithTable(const LockRequests& refLockRequestStructure); - /* - * This function implements the logic of checking the ownership of the - * lock from the releaselock request. - * - * Returns : True (if the requesting HMC & Session owns the lock(s)) - * Returns : False (if the request HMC or Session does not own the lock(s)) - */ - - virtual RcRelaseLock isItMyLock(const ListOfTransactionIds& refRids, - const SessionFlags& ids); - - /* - * This function validates the the list of transactionID's and returns false - * if the transaction ID is not valid & not present in the lock table - */ - - virtual bool validateRids(const ListOfTransactionIds& refRids); - - /* - * This function releases the locks that are already obtained by the - * requesting Management console. - */ - - void releaseLock(const ListOfTransactionIds& refRids); - - Lock() - { - transactionId = lockTable.empty() ? 0 : prev(lockTable.end())->first; - } - - /* - * This function implements the algorithm for checking the respective - * bytes of the resource id based on the lock management algorithm. - */ - - static bool checkByte(uint64_t resourceId1, uint64_t resourceId2, - uint32_t position); - - /* - * This functions implements a counter that generates a unique 32 bit - * number for every successful transaction. This number will be used by - * the Management Console for debug. - */ - virtual uint32_t generateTransactionId(); - - public: - /* - * Explicitly deleted copy and move constructors - */ - Lock(const Lock&) = delete; - Lock(Lock&&) = delete; - Lock& operator=(const Lock&) = delete; - Lock& operator=(Lock&&) = delete; - - /* - * This function implements the logic for acquiring a lock on a - * resource if the incoming request is legitimate without any - * conflicting requirements & without any conflicting requirement - * with the existing locks in the lock table. - * - */ - - RcAcquireLock acquireLock(const LockRequests& lockRequestStructure); - - /* - * This function implements the logic for releasing the lock that are - * owned by a management console session. - * - * The locks can be released by two ways - * - Using list of transaction ID's - * - Using a Session ID - * - * Client can choose either of the ways by using `Type` JSON key. - * - */ - RcReleaseLockApi releaseLock(const ListOfTransactionIds& p, - const SessionFlags& ids); - - /* - * This function implements the logic for getting the list of locks obtained - * by a particular management console. - */ - RcGetLockList getLockList(const ListOfSessionIds& listSessionId); - - /* - * This function is releases all the locks obtained by a particular - * session. - */ - - void releaseLock(const std::string& sessionId); - - static Lock& getInstance() - { - static Lock lockObject; - return lockObject; - } - - virtual ~Lock() = default; -}; - -inline RcGetLockList Lock::getLockList(const ListOfSessionIds& listSessionId) -{ - std::vector<std::pair<uint32_t, LockRequests>> lockList{}; - - if (!lockTable.empty()) - { - for (const auto& i : listSessionId) - { - auto it = lockTable.begin(); - while (it != lockTable.end()) - { - // Check if session id of this entry matches with session id - // given - if (std::get<0>(it->second[0]) == i) - { - BMCWEB_LOG_DEBUG("Session id is found in the locktable"); - - // Push the whole lock record into a vector for returning - // the json - lockList.emplace_back(it->first, it->second); - } - // Go to next entry in map - it++; - } - } - } - // we may have found at least one entry with the given session id - // return the json list of lock records pertaining to the given - // session id, or send an empty list if lock table is empty - return {lockList}; -} - -inline RcReleaseLockApi Lock::releaseLock(const ListOfTransactionIds& p, - const SessionFlags& ids) -{ - bool status = validateRids(p); - - if (!status) - { - // Validation of rids failed - BMCWEB_LOG_ERROR("releaseLock: Contains invalid request id"); - return std::make_pair(false, status); - } - // Validation passed, check if all the locks are owned by the - // requesting HMC - auto status2 = isItMyLock(p, ids); - if (!status2.first) - { - return std::make_pair(false, status2); - } - return std::make_pair(true, status2); -} - -inline RcAcquireLock Lock::acquireLock(const LockRequests& lockRequestStructure) -{ - // validate the lock request - - for (const auto& lockRecord : lockRequestStructure) - { - bool status = isValidLockRequest(lockRecord); - if (!status) - { - BMCWEB_LOG_DEBUG("Not a Valid record"); - BMCWEB_LOG_DEBUG("Bad json in request"); - return std::make_pair(true, std::make_pair(status, 0)); - } - } - // check for conflict record - - const LockRequests& multiRequest = lockRequestStructure; - bool status = isConflictRequest(multiRequest); - - if (status) - { - BMCWEB_LOG_DEBUG("There is a conflict within itself"); - return std::make_pair(true, std::make_pair(status, 1)); - } - BMCWEB_LOG_DEBUG("The request is not conflicting within itself"); - - // Need to check for conflict with the locktable entries. - - auto conflict = isConflictWithTable(multiRequest); - - BMCWEB_LOG_DEBUG("Done with checking conflict with the locktable"); - return std::make_pair(false, conflict); -} - -inline void Lock::releaseLock(const std::string& sessionId) -{ - if (!lockTable.empty()) - { - auto it = lockTable.begin(); - while (it != lockTable.end()) - { - if (!it->second.empty()) - { - // Check if session id of this entry matches with session id - // given - if (std::get<0>(it->second[0]) == sessionId) - { - BMCWEB_LOG_DEBUG("Remove the lock from the locktable " - "having sessionID={}", - sessionId); - BMCWEB_LOG_DEBUG("TransactionID ={}", it->first); - it = lockTable.erase(it); - } - else - { - it++; - } - } - } - } -} -inline RcRelaseLock Lock::isItMyLock(const ListOfTransactionIds& refRids, - const SessionFlags& ids) -{ - for (const auto& id : refRids) - { - // Just need to compare the client id of the first lock records in the - // complete lock row(in the map), because the rest of the lock records - // would have the same client id - - std::string expectedClientId = std::get<1>(lockTable[id][0]); - std::string expectedSessionId = std::get<0>(lockTable[id][0]); - - if ((expectedClientId == ids.first) && - (expectedSessionId == ids.second)) - { - // It is owned by the currently request hmc - BMCWEB_LOG_DEBUG("Lock is owned by the current hmc"); - // remove the lock - if (lockTable.erase(id) != 0U) - { - BMCWEB_LOG_DEBUG("Removing the locks with transaction ID : {}", - id); - } - else - { - BMCWEB_LOG_ERROR("Removing the locks from the lock table " - "failed, transaction ID: {}", - id); - } - } - else - { - BMCWEB_LOG_DEBUG("Lock is not owned by the current hmc"); - return std::make_pair(false, std::make_pair(id, lockTable[id][0])); - } - } - return std::make_pair(true, std::make_pair(0, LockRequest())); -} - -inline bool Lock::validateRids(const ListOfTransactionIds& refRids) -{ - for (const auto& id : refRids) - { - auto search = lockTable.find(id); - - if (search != lockTable.end()) - { - BMCWEB_LOG_DEBUG("Valid Transaction id"); - // continue for the next rid - } - else - { - BMCWEB_LOG_ERROR("validateRids: At least 1 inValid Request id: {}", - id); - return false; - } - } - return true; -} - -inline bool Lock::isValidLockRequest(const LockRequest& refLockRecord) -{ - // validate the locktype - - if (!((std::get<2>(refLockRecord) == "Read" || - (std::get<2>(refLockRecord) == "Write")))) - { - BMCWEB_LOG_DEBUG("Validation of LockType Failed"); - BMCWEB_LOG_DEBUG("Locktype : {}", std::get<2>(refLockRecord)); - return false; - } - - BMCWEB_LOG_DEBUG("{}", static_cast<int>(std::get<4>(refLockRecord).size())); - - // validate the number of segments - // Allowed No of segments are between 2 and 6 - if ((static_cast<int>(std::get<4>(refLockRecord).size()) > 6) || - (static_cast<int>(std::get<4>(refLockRecord).size()) < 2)) - { - BMCWEB_LOG_DEBUG("Validation of Number of Segments Failed"); - BMCWEB_LOG_DEBUG("Number of Segments provided : {}", - std::get<4>(refLockRecord).size()); - return false; - } - - int lockFlag = 0; - // validate the lockflags & segment length - - for (const auto& p : std::get<4>(refLockRecord)) - { - // validate the lock flags - // Allowed lockflags are locksame,lockall & dontlock - - if (!((p.first == "LockSame" || (p.first == "LockAll") || - (p.first == "DontLock")))) - { - BMCWEB_LOG_DEBUG("Validation of lock flags failed"); - BMCWEB_LOG_DEBUG("{}", p.first); - return false; - } - - // validate the segment length - // Allowed values of segment length are between 1 and 4 - - if (p.second < 1 || p.second > 4) - { - BMCWEB_LOG_DEBUG("Validation of Segment Length Failed"); - BMCWEB_LOG_DEBUG("{}", p.second); - return false; - } - - if ((p.first == "LockSame" || (p.first == "LockAll"))) - { - ++lockFlag; - if (lockFlag >= 2) - { - return false; - } - } - } - - return true; -} - -inline Rc Lock::isConflictWithTable(const LockRequests& refLockRequestStructure) -{ - if (lockTable.empty()) - { - uint32_t thisTransactionId = generateTransactionId(); - BMCWEB_LOG_DEBUG("{}", thisTransactionId); - // Lock table is empty, so we are safe to add the lockrecords - // as there will be no conflict - BMCWEB_LOG_DEBUG("Lock table is empty, so adding the lockrecords"); - lockTable.emplace(thisTransactionId, refLockRequestStructure); - - return std::make_pair(false, thisTransactionId); - } - BMCWEB_LOG_DEBUG( - "Lock table is not empty, check for conflict with lock table"); - // Lock table is not empty, compare the lockrequest entries with - // the entries in the lock table - - for (const auto& lockRecord1 : refLockRequestStructure) - { - for (const auto& map : lockTable) - { - for (const auto& lockRecord2 : map.second) - { - bool status = isConflictRecord(lockRecord1, lockRecord2); - if (status) - { - return std::make_pair( - true, std::make_pair(map.first, lockRecord2)); - } - } - } - } - - // Reached here, so no conflict with the locktable, so we are safe to - // add the request records into the lock table - - // Lock table is empty, so we are safe to add the lockrecords - // as there will be no conflict - BMCWEB_LOG_DEBUG(" Adding elements into lock table"); - transactionId = generateTransactionId(); - lockTable.emplace(std::make_pair(transactionId, refLockRequestStructure)); - - return std::make_pair(false, transactionId); -} - -inline bool Lock::isConflictRequest(const LockRequests& refLockRequestStructure) -{ - // check for all the locks coming in as a part of single request - // return conflict if any two lock requests are conflicting - - if (refLockRequestStructure.size() == 1) - { - BMCWEB_LOG_DEBUG("Only single lock request, so there is no conflict"); - // This means , we have only one lock request in the current - // request , so no conflict within the request - return false; - } - - BMCWEB_LOG_DEBUG( - "There are multiple lock requests coming in a single request"); - - // There are multiple requests a part of one request - - for (uint32_t i = 0; i < refLockRequestStructure.size(); i++) - { - for (uint32_t j = i + 1; j < refLockRequestStructure.size(); j++) - { - const LockRequest& p = refLockRequestStructure[i]; - const LockRequest& q = refLockRequestStructure[j]; - bool status = isConflictRecord(p, q); - - if (status) - { - return true; - } - } - } - return false; -} - -// This function converts the provided uint64_t resource id's from the two -// lock requests subjected for comparison, and this function also compares -// the content by bytes mentioned by a uint32_t number. - -// If all the elements in the lock requests which are subjected for comparison -// are same, then the last comparison would be to check for the respective -// bytes in the resourceid based on the segment length. -inline bool Lock::checkByte(uint64_t resourceId1, uint64_t resourceId2, - uint32_t position) -{ - if (position >= sizeof(resourceId1)) - { - return false; - } - - uint8_t pPosition = 0; - uint8_t qPosition = 0; - pPosition = 0xFF & (resourceId1 >> (8 * position)); - qPosition = 0xFF & (resourceId2 >> (8 * position)); - - return pPosition == qPosition; -} - -inline bool Lock::isConflictRecord(const LockRequest& refLockRecord1, - const LockRequest& refLockRecord2) -{ - // No conflict if both are read locks - - if (std::get<2>(refLockRecord1) == "Read" && - std::get<2>(refLockRecord2) == "Read") - { - BMCWEB_LOG_DEBUG("Both are read locks, no conflict"); - return false; - } - - uint32_t i = 0; - for (const auto& p : std::get<4>(refLockRecord1)) - { - // return conflict when any of them is try to lock all resources - // under the current resource level. - if (p.first == "LockAll" || - std::get<4>(refLockRecord2)[i].first == "LockAll") - { - BMCWEB_LOG_DEBUG( - "Either of the Comparing locks are trying to lock all " - "resources under the current resource level"); - return true; - } - - // determine if there is a lock-all-with-same-segment-size. - // If the current segment sizes are the same,then we should fail. - - if ((p.first == "LockSame" || - std::get<4>(refLockRecord2)[i].first == "LockSame") && - (p.second == std::get<4>(refLockRecord2)[i].second)) - { - return true; - } - - // if segment lengths are not the same, it means two different locks - // So no conflict - if (p.second != std::get<4>(refLockRecord2)[i].second) - { - BMCWEB_LOG_DEBUG("Segment lengths are not same"); - BMCWEB_LOG_DEBUG("Segment 1 length : {}", p.second); - BMCWEB_LOG_DEBUG("Segment 2 length : {}", - std::get<4>(refLockRecord2)[i].second); - return false; - } - - // compare segment data - - for (uint32_t j = 0; j < p.second; j++) - { - // if the segment data is different, then the locks is on a - // different resource so no conflict between the lock - // records. - // BMC is little endian, but the resourceID is formed by - // the Management Console in such a way that, the first byte - // from the MSB Position corresponds to the First Segment - // data. Therefore we need to convert the incoming - // resourceID into Big Endian before processing further. - if (!(checkByte( - boost::endian::endian_reverse(std::get<3>(refLockRecord1)), - boost::endian::endian_reverse(std::get<3>(refLockRecord2)), - j))) - { - return false; - } - } - - ++i; - } - - return true; -} - -inline uint32_t Lock::generateTransactionId() -{ - ++transactionId; - return transactionId; -} - -} // namespace ibm_mc_lock -} // namespace crow |