Skip to content
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

[IM] Update read transaction resource minimas #18884

Merged
merged 10 commits into from
Jun 16, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
275 changes: 234 additions & 41 deletions src/app/InteractionModelEngine.cpp

Large diffs are not rendered by default.

173 changes: 107 additions & 66 deletions src/app/InteractionModelEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,11 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
* Spec 8.5.1 A publisher SHALL always ensure that every fabric the node is commissioned into can create at least three
* subscriptions to the publisher and that each subscription SHALL support at least 3 attribute/event paths.
*/
static constexpr size_t kMinSupportedSubscriptionsPerFabric = 2;
static constexpr size_t kMinSupportedPathsPerSubscription = 2;
static constexpr size_t kReservedPathsPerReadRequest = 9;
static constexpr size_t kReservedReadHandlersPerFabricForReadRequests = 1;
static constexpr size_t kMinSupportedSubscriptionsPerFabric = 2;
static constexpr size_t kMinSupportedPathsPerSubscription = 2;
static constexpr size_t kMinSupportedPathsPerReadRequest = 9;
static constexpr size_t kMinSupportedReadRequestsPerFabric = 1;
static constexpr size_t kReadHandlerPoolSize = CHIP_IM_MAX_NUM_SUBSCRIPTIONS + CHIP_IM_MAX_NUM_READS;

// TODO: Per spec, the above numbers should be 3, 3, 9, 1, however, we use a lower limit to reduce the memory usage and should
// fix it when we have reduced the memory footprint of ReadHandlers.
Expand Down Expand Up @@ -242,21 +243,6 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
*/
bool HasConflictWriteRequests(const WriteHandler * apWriteHandler, const ConcreteAttributePath & aPath);

/**
* We only allow one active read transaction per fabric, and the number of paths used is limited by
* kReservedPathsPerReadRequest. This function will check if the given ReadHandler will exceed the limitations for the accessing
* fabric.
*
* If CHIP_NO_ERROR is returned, it's OK to proceed with the read.
*
* Otherwise the CHIP_ERROR encodes an interaction model status that needs
* to turn into a Status Response to the client.
*
* TODO: (#17418) We are now reserving resources for read requests, could be changed to similar algorithm for read resources
* minimas.
*/
CHIP_ERROR CanEstablishReadTransaction(const ReadHandler * apReadHandler);

/**
* Select the oldest (and the one that exceeds the per subscription resource minimum if there are any) read handler on the
* fabric with the given fabric index. Evict it when the fabric uses more resources than the per fabric quota or aForceEvict is
Expand All @@ -266,6 +252,14 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
*/
bool TrimFabric(FabricIndex aFabricIndex, bool aForceEvict);

/**
* Select the oldest (and the one that exceeds the per read resource minimum if there are any) read handler on the fabric with
* the given fabric index.
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
*
* @retval Whether we have evicted a read transaction.
*/
bool TrimFabricForRead(FabricIndex aFabricIndex);

uint16_t GetMinSubscriptionsPerFabric() const;

#if CONFIG_IM_BUILD_FOR_UNIT_TEST
Expand All @@ -275,48 +269,29 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
auto & GetReadHandlerPool() { return mReadHandlers; }

//
// Override the maximal capacity of the underlying read handler pool to mimic
// out of memory scenarios in unit-tests.
//
// This function did not considered the resources reserved for read handlers,
// SetHandlerCapacityForSubscriptions if there are subscriptions in the tests.
//
// If -1 is passed in, no override is instituted and default behavior resumes.
//
void SetHandlerCapacity(int32_t sz) { mReadHandlerCapacityOverride = sz; }

//
// Override the maximal capacity of the underlying attribute path pool and event path pool to mimic
// out of paths exhausted scenarios in unit-tests.
//
// This function did not considered the resources reserved for read handlers,
// SetPathPoolCapacityForSubscriptions if there are subscriptions in the tests.
// Override the maximal capacity of the fabric table only for interaction model engine
//
// If -1 is passed in, no override is instituted and default behavior resumes.
//
void SetPathPoolCapacity(int32_t sz) { mPathPoolCapacityOverride = sz; }
void SetConfigMaxFabrics(int32_t sz) { mMaxNumFabricsOverride = sz; }

//
// Override the maximal capacity of the underlying read handler pool to mimic
// out of memory scenarios in unit-tests.
//
// If -1 is passed in, no override is instituted and default behavior resumes.
//
void SetHandlerCapacityForSubscriptions(int32_t sz)
{
SetHandlerCapacity(sz == -1 ? -1 : sz + static_cast<int32_t>(kReservedHandlersForReads));
}
void SetHandlerCapacityForReads(int32_t sz) { mReadHandlerCapacityForReadsOverride = sz; }
void SetHandlerCapacityForSubscriptions(int32_t sz) { mReadHandlerCapacityForSubscriptionsOverride = sz; }

//
// Override the maximal capacity of the underlying attribute path pool and event path pool to mimic
// out of paths exhausted scenarios in unit-tests.
//
// If -1 is passed in, no override is instituted and default behavior resumes.
//
void SetPathPoolCapacityForSubscriptions(int32_t sz)
{
SetPathPoolCapacity(sz == -1 ? -1 : sz + static_cast<int32_t>(kReservedPathsForReads));
}
void SetPathPoolCapacityForReads(int32_t sz) { mPathPoolCapacityForReadsOverride = sz; }
void SetPathPoolCapacityForSubscriptions(int32_t sz) { mPathPoolCapacityForSubscriptionsOverride = sz; }

//
// We won't limit the handler used per fabric on platforms that are using heap for memory pools, so we introduces a flag to
Expand Down Expand Up @@ -416,23 +391,53 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
CHIP_ERROR ShutdownExistingSubscriptionsIfNeeded(Messaging::ExchangeContext * apExchangeContext,
System::PacketBufferHandle && aPayload);

inline size_t GetPathPoolCapacity() const
inline size_t GetPathPoolCapacityForReads() const
{
#if CONFIG_IM_BUILD_FOR_UNIT_TEST
return (mPathPoolCapacityOverride == -1) ? CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS
: static_cast<size_t>(mPathPoolCapacityOverride);
return (mPathPoolCapacityForReadsOverride == -1) ? CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS
: static_cast<size_t>(mPathPoolCapacityForReadsOverride);
#else
return CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS;
return CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS;
#endif
}

inline size_t GetReadHandlerPoolCapacity() const
inline size_t GetReadHandlerPoolCapacityForReads() const
{
#if CONFIG_IM_BUILD_FOR_UNIT_TEST
return (mReadHandlerCapacityOverride == -1) ? CHIP_IM_MAX_NUM_READ_HANDLER
: static_cast<size_t>(mReadHandlerCapacityOverride);
return (mReadHandlerCapacityForReadsOverride == -1) ? CHIP_IM_MAX_NUM_READS
: static_cast<size_t>(mReadHandlerCapacityForReadsOverride);
#else
return CHIP_IM_MAX_NUM_READ_HANDLER;
return CHIP_IM_MAX_NUM_READS;
#endif
}

inline size_t GetPathPoolCapacityForSubscriptions() const
{
#if CONFIG_IM_BUILD_FOR_UNIT_TEST
return (mPathPoolCapacityForSubscriptionsOverride == -1) ? CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS
: static_cast<size_t>(mPathPoolCapacityForSubscriptionsOverride);
#else
return CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS;
#endif
}

inline size_t GetReadHandlerPoolCapacityForSubscriptions() const
{
#if CONFIG_IM_BUILD_FOR_UNIT_TEST
return (mReadHandlerCapacityForSubscriptionsOverride == -1)
? CHIP_IM_MAX_NUM_SUBSCRIPTIONS
: static_cast<size_t>(mReadHandlerCapacityForSubscriptionsOverride);
#else
return CHIP_IM_MAX_NUM_SUBSCRIPTIONS;
#endif
}

inline uint8_t GetConfigMaxFabrics() const
{
#if CONFIG_IM_BUILD_FOR_UNIT_TEST
return (mMaxNumFabricsOverride == -1) ? CHIP_CONFIG_MAX_FABRICS : static_cast<uint8_t>(mMaxNumFabricsOverride);
#else
return CHIP_CONFIG_MAX_FABRICS;
#endif
}

Expand All @@ -451,6 +456,27 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
bool EnsureResourceForSubscription(FabricIndex aFabricIndex, size_t aRequestedAttributePathCount,
size_t aRequestedEventPathCount);

/**
* Verify and ensure (by killing oldest read handlers that make the resources used by the current fabric exceed the fabric
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
* quota)
* - PASE sessions will be counted in a virtual fabric (i.e. kInvalidFabricIndex will be consided as a "valid" fabric in this
* function)
* - If the existing resources can serve this read transaction, this function will return true.
* - or if the resources used by read transactions in the fabric index meets the per fabric resource limit (i.e. 9 paths & 1
* read) after accepting this read request, this function will always return true by evicting existing read transactions which
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
* uses more resources than kMinSupportedPathsPerReadRequest from the oldest to the newest.
* - or if the resources used by read transactions in the fabric index will exceed the per fabric resource limit (i.e. 9 paths &
* 1 read) after accepting this read request, this function will return false without evicting any existing transaction.
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
* - However, read transactions on PASE sessions won't evict any existing read transactions when we have already commissioned
* CHIP_CONFIG_MAX_FABRICS fabrics on the device.
*
* @retval CHIP_NO_ERROR: The read transaction can be accepted.
* @retval CHIP_IM_GLOBAL_STATUS(ResourceExhausted): The read handler pool is exhausted.
* @retval CHIP_IM_GLOBAL_STATUS(PathsExhausted): The attribute / event path pool is exhausted.
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
*/
Protocols::InteractionModel::Status EnsureResourceForRead(FabricIndex aFabricIndex, size_t aRequestedAttributePathCount,
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
size_t aRequestedEventPathCount);

template <typename T, size_t N>
void ReleasePool(ObjectList<T> *& aObjectList, ObjectPool<ObjectList<T>, N> & aObjectPool);
template <typename T, size_t N>
Expand All @@ -465,31 +491,46 @@ class InteractionModelEngine : public Messaging::UnsolicitedMessageHandler,
WriteHandler mWriteHandlers[CHIP_IM_MAX_NUM_WRITE_HANDLER];
reporting::Engine mReportingEngine;

static constexpr size_t kReservedHandlersForReads = kReservedReadHandlersPerFabricForReadRequests * (CHIP_CONFIG_MAX_FABRICS);
static constexpr size_t kReservedPathsForReads = kReservedPathsPerReadRequest * kReservedHandlersForReads;
static constexpr size_t kReservedHandlersForReads = kMinSupportedReadRequestsPerFabric * (CHIP_CONFIG_MAX_FABRICS);
static constexpr size_t kReservedPathsForReads = kMinSupportedPathsPerReadRequest * kReservedHandlersForReads;

#if !CHIP_SYSTEM_CONFIG_POOL_USE_HEAP
static_assert(CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS >= CHIP_CONFIG_MAX_FABRICS *
(kMinSupportedPathsPerSubscription * kMinSupportedSubscriptionsPerFabric + kReservedPathsPerReadRequest),
"CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS is too small to match the requirements of spec 8.5.1");
static_assert(CHIP_IM_MAX_NUM_READ_HANDLER >= CHIP_CONFIG_MAX_FABRICS *
(kMinSupportedSubscriptionsPerFabric + kReservedReadHandlersPerFabricForReadRequests),
"CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS is too small to match the requirements of spec 8.5.1");
static_assert(CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS >=
CHIP_CONFIG_MAX_FABRICS * (kMinSupportedPathsPerSubscription * kMinSupportedSubscriptionsPerFabric),
"CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS is too small to match the requirements of spec 8.5.1");
static_assert(CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS >=
CHIP_CONFIG_MAX_FABRICS * (kMinSupportedReadRequestsPerFabric * kMinSupportedPathsPerReadRequest),
"CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS is too small to match the requirements of spec 8.5.1");
static_assert(CHIP_IM_MAX_NUM_SUBSCRIPTIONS >= CHIP_CONFIG_MAX_FABRICS * kMinSupportedSubscriptionsPerFabric,
"CHIP_IM_MAX_NUM_SUBSCRIPTIONS is too small to match the requirements of spec 8.5.1");
static_assert(CHIP_IM_MAX_NUM_READS >= CHIP_CONFIG_MAX_FABRICS * kMinSupportedReadRequestsPerFabric,
"CHIP_IM_MAX_NUM_READS is too small to match the requirements of spec 8.5.1");
#endif

ObjectPool<ObjectList<AttributePathParams>, CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS> mAttributePathPool;
ObjectPool<ObjectList<EventPathParams>, CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS> mEventPathPool;
ObjectPool<ObjectList<DataVersionFilter>, CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS> mDataVersionFilterPool;
ObjectPool<ObjectList<AttributePathParams>,
CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS + CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS>
mAttributePathPool;
ObjectPool<ObjectList<EventPathParams>,
CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS + CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS>
mEventPathPool;
ObjectPool<ObjectList<DataVersionFilter>,
CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_READS + CHIP_IM_SERVER_MAX_NUM_PATH_GROUPS_FOR_SUBSCRIPTIONS>
mDataVersionFilterPool;

ObjectPool<ReadHandler, CHIP_IM_MAX_NUM_READ_HANDLER> mReadHandlers;
ObjectPool<ReadHandler, CHIP_IM_MAX_NUM_READS + CHIP_IM_MAX_NUM_SUBSCRIPTIONS> mReadHandlers;

ReadClient * mpActiveReadClientList = nullptr;

ReadHandler::ApplicationCallback * mpReadHandlerApplicationCallback = nullptr;

#if CONFIG_IM_BUILD_FOR_UNIT_TEST
int mReadHandlerCapacityOverride = -1;
int mPathPoolCapacityOverride = -1;
int mReadHandlerCapacityForSubscriptionsOverride = -1;
int mPathPoolCapacityForSubscriptionsOverride = -1;

int mReadHandlerCapacityForReadsOverride = -1;
int mPathPoolCapacityForReadsOverride = -1;

int mMaxNumFabricsOverride = -1;

// We won't limit the handler used per fabric on platforms that are using heap for memory pools, so we introduces a flag to
// enforce such check based on the configured size. This flag is used for unit tests only, there is another compare time flag
Expand Down
12 changes: 4 additions & 8 deletions src/app/ReadHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ ReadHandler::ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeCon
InteractionType aInteractionType) :
mManagementCallback(apCallback)
{
mpExchangeCtx = apExchangeContext;
mInteractionType = aInteractionType;
mLastWrittenEventsBytes = 0;
mSubscriptionStartGeneration = InteractionModelEngine::GetInstance()->GetReportingEngine().GetDirtySetGeneration();
mpExchangeCtx = apExchangeContext;
mInteractionType = aInteractionType;
mLastWrittenEventsBytes = 0;
mTransactionStartGeneration = InteractionModelEngine::GetInstance()->GetReportingEngine().GetDirtySetGeneration();
mFlags.ClearAll();
mFlags.Set(ReadHandlerFlags::PrimingReports, true);
if (apExchangeContext != nullptr)
Expand Down Expand Up @@ -384,10 +384,6 @@ CHIP_ERROR ReadHandler::ProcessReadRequest(System::PacketBufferHandle && aPayloa
}
ReturnErrorOnFailure(err);

// Ensure the read transaction doesn't exceed the resources dedicated to
// read transactions.
ReturnErrorOnFailure(InteractionModelEngine::GetInstance()->CanEstablishReadTransaction(this));

bool isFabricFiltered;
ReturnErrorOnFailure(readRequestParser.GetIsFabricFiltered(&isFabricFiltered));
mFlags.Set(ReadHandlerFlags::FabricFiltered, isFabricFiltered);
Expand Down
4 changes: 2 additions & 2 deletions src/app/ReadHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ class ReadHandler : public Messaging::ExchangeDelegate
Transport::SecureSession * GetSession() const;
SubjectDescriptor GetSubjectDescriptor() const { return GetSession()->GetSubjectDescriptor(); }

auto GetSubscriptionStartGeneration() const { return mSubscriptionStartGeneration; }
auto GetTransactionStartGeneration() const { return mTransactionStartGeneration; }

void UnblockUrgentEventDelivery()
{
Expand Down Expand Up @@ -426,7 +426,7 @@ class ReadHandler : public Messaging::ExchangeDelegate

// When we don't have enough resources for a new subscription, the oldest subscription might be evicted by interaction model
// engine, the "oldest" subscription is the subscription with the smallest generation.
uint64_t mSubscriptionStartGeneration = 0;
uint64_t mTransactionStartGeneration = 0;

SubscriptionId mSubscriptionId = 0;
uint16_t mMinIntervalFloorSeconds = 0;
Expand Down
Loading