diff --git a/preempt/CMakeLists.txt b/preempt/CMakeLists.txt index e88c3aee..17a53a31 100644 --- a/preempt/CMakeLists.txt +++ b/preempt/CMakeLists.txt @@ -40,3 +40,7 @@ target_link_libraries(preempt PRIVATE sched PRIVATE protocol ) + +if(BUILD_TEST) + add_subdirectory(test) +endif() diff --git a/preempt/src/xqueue/xqueue.cpp b/preempt/src/xqueue/xqueue.cpp index 45ca7b61..39f6bb70 100644 --- a/preempt/src/xqueue/xqueue.cpp +++ b/preempt/src/xqueue/xqueue.cpp @@ -24,6 +24,14 @@ XResult XQueueManager::Add(XQueueHandle *xq_hp, HwQueueHandle hwq_h, int64_t lev return kXSchedErrorNotFound; } + if (level > hwq_shptr->GetMaxSupportedLevel()) { + XWARN("preempt level %d is not supported by HwQueue 0x" FMT_64X + ", max supported level is %d", + static_cast(level), hwq_h, + hwq_shptr->GetMaxSupportedLevel()); + return kXSchedErrorNotSupported; + } + if (hwq_shptr->GetXQueue() != nullptr) { XQueueHandle xq_h = hwq_shptr->GetXQueue()->GetHandle(); auto it = xqs_.find(xq_h); @@ -285,6 +293,12 @@ EXPORT_C_FUNC XResult XQueueSetPreemptLevel(XQueueHandle xq, XPreemptLevel level XWARN("XQueue with handle 0x" FMT_64X " does not support dynamic level", xq); return kXSchedErrorNotSupported; } + const auto hwq_shptr = xq_shptr->GetHwQueue(); + if (hwq_shptr == nullptr || level > hwq_shptr->GetMaxSupportedLevel()) { + XWARN("preempt level %d is not supported by XQueue 0x" FMT_64X, + level, xq); + return kXSchedErrorNotSupported; + } xq_shptr->SetPreemptLevel(level); return kXSchedSuccess; } diff --git a/preempt/test/CMakeLists.txt b/preempt/test/CMakeLists.txt new file mode 100644 index 00000000..7ed240a4 --- /dev/null +++ b/preempt/test/CMakeLists.txt @@ -0,0 +1,14 @@ +add_executable(preempt_level_capability_test + preempt_level_capability_test.cpp +) +target_compile_options(preempt_level_capability_test PRIVATE + -Wall + -Wextra + -Werror +) +target_link_libraries(preempt_level_capability_test PRIVATE + preempt + utils +) +add_test(NAME preempt_level_capability_test + COMMAND preempt_level_capability_test) diff --git a/preempt/test/preempt_level_capability_test.cpp b/preempt/test/preempt_level_capability_test.cpp new file mode 100644 index 00000000..ef7991b5 --- /dev/null +++ b/preempt/test/preempt_level_capability_test.cpp @@ -0,0 +1,90 @@ +#include +#include +#include + +#include "xsched/preempt/hal/hw_queue.h" +#include "xsched/xqueue.h" + +namespace +{ + +using namespace xsched::preempt; + +class CapabilityQueue final : public HwQueue +{ +public: + virtual void Launch(std::shared_ptr) override { } + virtual void Synchronize() override { } + virtual void Deactivate() override { ++deactivate_count; } + virtual void Reactivate(const CommandLog &) override + { + ++reactivate_count; + } + + virtual XDevice GetDevice() override { return 0; } + virtual HwQueueHandle GetHandle() override { return kHandle; } + virtual bool SupportDynamicLevel() override { return true; } + virtual XPreemptLevel GetMaxSupportedLevel() override + { + return kPreemptLevelDeactivate; + } + + static constexpr HwQueueHandle kHandle = 0xc0ffee; + int deactivate_count = 0; + int reactivate_count = 0; +}; + +bool Expect(bool condition, const std::string &message) +{ + if (condition) return true; + std::cerr << "FAIL: " << message << '\n'; + return false; +} + +} // namespace + +int main() +{ + using namespace xsched::preempt; + + auto queue = std::make_shared(); + bool ok = Expect( + HwQueueManager::Add(CapabilityQueue::kHandle, + [queue]() { return queue; }) + == kXSchedSuccess, + "register Level-2 HwQueue"); + + XQueueHandle xqueue = 0; + ok &= Expect(XQueueCreate(&xqueue, CapabilityQueue::kHandle, + kPreemptLevelInterrupt, + kQueueCreateFlagNone) + == kXSchedErrorNotSupported, + "reject Level-3 creation on a Level-2 HwQueue"); + ok &= Expect(XQueueCreate(&xqueue, CapabilityQueue::kHandle, + kPreemptLevelDeactivate, + kQueueCreateFlagNone) + == kXSchedSuccess, + "create Level-2 XQueue"); + if (xqueue != 0) { + ok &= Expect(XQueueSetPreemptLevel(xqueue, + kPreemptLevelInterrupt) + == kXSchedErrorNotSupported, + "reject dynamic upgrade beyond Level-2"); + ok &= Expect(XQueueSuspend(xqueue, kQueueSuspendFlagNone) == kXSchedSuccess, + "suspend Level-2 XQueue"); + ok &= Expect(queue->deactivate_count == 1, + "suspend must call Deactivate"); + ok &= Expect(XQueueResume(xqueue, kQueueResumeFlagNone) == kXSchedSuccess, + "resume Level-2 XQueue"); + ok &= Expect(queue->reactivate_count == 1, + "resume must call Reactivate"); + ok &= Expect(XQueueDestroy(xqueue) == kXSchedSuccess, + "destroy Level-2 XQueue"); + } + ok &= Expect(HwQueueDestroy(CapabilityQueue::kHandle) == kXSchedSuccess, + "destroy Level-2 HwQueue"); + + if (!ok) return 1; + std::cout << "HwQueue preemption capability tests passed.\n"; + return 0; +}