2021-07-02 14:41:38 -07:00
|
|
|
/* Copyright (c) 2012 - 2021 Advanced Micro Devices, Inc.
|
2020-02-04 09:26:14 -08:00
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
|
|
|
all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
|
THE SOFTWARE. */
|
2014-07-04 16:17:05 -04:00
|
|
|
|
|
|
|
|
#include "commandqueue.hpp"
|
|
|
|
|
#include "thread/monitor.hpp"
|
|
|
|
|
#include "device/device.hpp"
|
|
|
|
|
#include "platform/context.hpp"
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* \file commandQueue.cpp
|
|
|
|
|
* \brief Definitions for HostQueue object.
|
|
|
|
|
*
|
2020-03-11 21:26:07 -04:00
|
|
|
* \author Laurent Morichetti
|
2014-07-04 16:17:05 -04:00
|
|
|
* \date October 2008
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
namespace amd {
|
|
|
|
|
|
2022-08-12 16:02:29 -04:00
|
|
|
HostQueue::HostQueue(Context& context, Device& device, cl_command_queue_properties props,
|
2020-05-12 10:31:07 -04:00
|
|
|
uint queueRTCUs, Priority priority, const std::vector<uint32_t>& cuMask)
|
2022-08-12 16:02:29 -04:00
|
|
|
: CommandQueue(context, device, props, device.info().queueProperties_, queueRTCUs,
|
2020-08-24 17:55:41 -07:00
|
|
|
priority, cuMask),
|
2020-11-26 15:49:25 -05:00
|
|
|
lastEnqueueCommand_(nullptr),
|
|
|
|
|
head_(nullptr),
|
2021-12-01 09:17:04 -08:00
|
|
|
tail_(nullptr),
|
2023-11-01 05:22:47 +00:00
|
|
|
isActive_(false) {
|
2023-08-24 17:36:00 +00:00
|
|
|
if (GPU_FORCE_QUEUE_PROFILING) {
|
|
|
|
|
properties().set(CL_QUEUE_PROFILING_ENABLE);
|
|
|
|
|
}
|
2020-11-30 17:49:25 -05:00
|
|
|
if (AMD_DIRECT_DISPATCH) {
|
2020-11-26 15:49:25 -05:00
|
|
|
// Initialize the queue
|
2020-11-30 17:49:25 -05:00
|
|
|
thread_.Init(this);
|
|
|
|
|
} else {
|
|
|
|
|
if (thread_.state() >= Thread::INITIALIZED) {
|
|
|
|
|
ScopedLock sl(queueLock_);
|
|
|
|
|
thread_.start(this);
|
|
|
|
|
queueLock_.wait();
|
|
|
|
|
}
|
2017-04-13 13:56:38 -04:00
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
}
|
|
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
bool HostQueue::terminate() {
|
2020-11-30 17:49:25 -05:00
|
|
|
if (AMD_DIRECT_DISPATCH) {
|
2023-06-27 20:57:49 +05:30
|
|
|
if (vdev() != nullptr) {
|
|
|
|
|
Command* marker = new Marker(*this, true);
|
|
|
|
|
if (marker != nullptr) {
|
|
|
|
|
marker->enqueue();
|
|
|
|
|
marker->awaitCompletion();
|
|
|
|
|
marker->release();
|
|
|
|
|
}
|
2021-05-19 09:58:39 -04:00
|
|
|
}
|
2023-02-16 15:48:57 -05:00
|
|
|
thread_.Release();
|
2021-05-19 09:58:39 -04:00
|
|
|
thread_.acceptingCommands_ = false;
|
2020-11-30 17:49:25 -05:00
|
|
|
} else {
|
|
|
|
|
if (Os::isThreadAlive(thread_)) {
|
|
|
|
|
Command* marker = nullptr;
|
|
|
|
|
// Send a finish if the queue is still accepting commands.
|
2023-01-12 17:55:43 +00:00
|
|
|
if (lastEnqueueCommand_ != nullptr || !amd::IS_HIP) {
|
2020-11-30 17:49:25 -05:00
|
|
|
ScopedLock sl(queueLock_);
|
|
|
|
|
if (thread_.acceptingCommands_) {
|
|
|
|
|
marker = new Marker(*this, false);
|
|
|
|
|
if (marker != nullptr) {
|
|
|
|
|
append(*marker);
|
|
|
|
|
queueLock_.notify();
|
|
|
|
|
}
|
2020-08-24 17:59:46 -07:00
|
|
|
}
|
2018-05-07 18:50:50 -04:00
|
|
|
}
|
2020-11-30 17:49:25 -05:00
|
|
|
if (marker != nullptr) {
|
2023-01-12 17:55:43 +00:00
|
|
|
if (marker->notifyCmdQueue()) {
|
|
|
|
|
while (marker->status() > CL_COMPLETE && Os::isThreadAlive(thread_)) {
|
|
|
|
|
amd::Os::yield();
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-11-30 17:49:25 -05:00
|
|
|
marker->release();
|
|
|
|
|
}
|
2018-05-07 18:50:50 -04:00
|
|
|
|
2020-11-30 17:49:25 -05:00
|
|
|
// Wake-up the command loop, so it can exit
|
|
|
|
|
{
|
|
|
|
|
ScopedLock sl(queueLock_);
|
|
|
|
|
thread_.acceptingCommands_ = false;
|
|
|
|
|
queueLock_.notify();
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2020-11-30 17:49:25 -05:00
|
|
|
// FIXME_lmoriche: fix termination handshake
|
2023-01-12 17:55:43 +00:00
|
|
|
while (thread_.state() < Thread::FINISHED && Os::isThreadAlive(thread_)) {
|
2020-11-30 17:49:25 -05:00
|
|
|
Os::yield();
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
}
|
2017-04-13 13:56:38 -04:00
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
if (Agent::shouldPostCommandQueueEvents()) {
|
|
|
|
|
Agent::postCommandQueueFree(as_cl(this->asCommandQueue()));
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
return true;
|
2014-07-04 16:17:05 -04:00
|
|
|
}
|
|
|
|
|
|
2023-09-21 15:47:56 -04:00
|
|
|
void HostQueue::finish(bool cpu_wait) {
|
2020-02-20 16:24:10 -05:00
|
|
|
Command* command = nullptr;
|
|
|
|
|
if (IS_HIP) {
|
2020-02-21 16:43:21 -05:00
|
|
|
command = getLastQueuedCommand(true);
|
2022-10-21 17:49:08 -04:00
|
|
|
if (command == nullptr) {
|
2021-06-23 11:41:52 -04:00
|
|
|
return;
|
|
|
|
|
}
|
2020-02-20 16:24:10 -05:00
|
|
|
}
|
2023-09-28 15:00:40 -04:00
|
|
|
// If command doesn't contain HW event and runtime didn't request CPU wait,
|
|
|
|
|
// then force marker submit
|
|
|
|
|
bool force_marker = false;
|
2023-10-05 17:33:31 -04:00
|
|
|
// Force CPU wait if profiler is enabled. Pytorch tests may use tracer's plugin and rely on
|
|
|
|
|
// profiling information to be available right after finish.
|
2023-10-06 13:39:41 -04:00
|
|
|
cpu_wait |= activity_prof::IsEnabled(OP_ID_DISPATCH);
|
2023-09-28 15:00:40 -04:00
|
|
|
if (AMD_DIRECT_DISPATCH && (command != nullptr) && !cpu_wait) {
|
|
|
|
|
void* hw_event =
|
|
|
|
|
(command->NotifyEvent() != nullptr) ? command->NotifyEvent()->HwEvent() : command->HwEvent();
|
|
|
|
|
force_marker = (hw_event == nullptr);
|
|
|
|
|
}
|
2023-11-21 03:41:49 +00:00
|
|
|
if (nullptr == command || force_marker ||
|
|
|
|
|
vdev()->isHandlerPending() || vdev()->isFenceDirty()) {
|
2022-04-21 15:57:44 -07:00
|
|
|
if (nullptr != command) {
|
|
|
|
|
command->release();
|
|
|
|
|
}
|
2020-02-20 16:24:10 -05:00
|
|
|
// Send a finish to make sure we finished all commands
|
|
|
|
|
command = new Marker(*this, false);
|
|
|
|
|
if (command == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-09-20 21:56:02 -07:00
|
|
|
ClPrint(LOG_DEBUG, LOG_CMD, "Marker queued to ensure finish");
|
2020-02-20 16:24:10 -05:00
|
|
|
command->enqueue();
|
2021-06-07 15:56:47 -04:00
|
|
|
}
|
|
|
|
|
// Check HW status of the ROCcrl event. Note: not all ROCclr modes support HW status
|
|
|
|
|
static constexpr bool kWaitCompletion = true;
|
2023-09-21 15:47:56 -04:00
|
|
|
if (cpu_wait || !device().IsHwEventReady(command->event(), kWaitCompletion)) {
|
2021-06-16 22:11:26 -07:00
|
|
|
ClPrint(LOG_DEBUG, LOG_CMD, "HW Event not ready, awaiting completion instead");
|
2020-02-20 16:24:10 -05:00
|
|
|
command->awaitCompletion();
|
2017-04-13 13:56:38 -04:00
|
|
|
}
|
2021-06-16 09:13:58 -07:00
|
|
|
if (IS_HIP) {
|
|
|
|
|
ScopedLock sl(vdev()->execution());
|
|
|
|
|
ScopedLock l(lastCmdLock_);
|
2022-07-05 13:06:52 -04:00
|
|
|
// Runtime can clear the last command only if no other submissions occured during finish()
|
|
|
|
|
if (command == lastEnqueueCommand_) {
|
2021-06-16 09:13:58 -07:00
|
|
|
lastEnqueueCommand_->release();
|
|
|
|
|
lastEnqueueCommand_ = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-19 23:09:34 -05:00
|
|
|
command->release();
|
2019-11-04 14:44:59 -05:00
|
|
|
ClPrint(LOG_DEBUG, LOG_CMD, "All commands finished");
|
2014-07-04 16:17:05 -04:00
|
|
|
}
|
|
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
void HostQueue::loop(device::VirtualDevice* virtualDevice) {
|
|
|
|
|
// Notify the caller that the queue is ready to accept commands.
|
|
|
|
|
{
|
|
|
|
|
ScopedLock sl(queueLock_);
|
|
|
|
|
thread_.acceptingCommands_ = true;
|
|
|
|
|
queueLock_.notify();
|
|
|
|
|
}
|
|
|
|
|
// Create a command batch with all the commands present in the queue.
|
|
|
|
|
Command* head = NULL;
|
|
|
|
|
Command* tail = NULL;
|
|
|
|
|
while (true) {
|
|
|
|
|
// Get one command from the queue
|
|
|
|
|
Command* command = queue_.dequeue();
|
|
|
|
|
if (command == NULL) {
|
|
|
|
|
ScopedLock sl(queueLock_);
|
|
|
|
|
while ((command = queue_.dequeue()) == NULL) {
|
|
|
|
|
if (!thread_.acceptingCommands_) {
|
|
|
|
|
return;
|
2014-07-04 16:17:05 -04:00
|
|
|
}
|
2017-04-13 13:56:38 -04:00
|
|
|
queueLock_.wait();
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
command->retain();
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
// Process the command's event wait list.
|
|
|
|
|
const Command::EventWaitList& events = command->eventWaitList();
|
|
|
|
|
bool dependencyFailed = false;
|
2022-06-22 19:39:43 +00:00
|
|
|
ClPrint(LOG_DEBUG, LOG_CMD, "Command (%s) processing: %p ,events.size(): %d",
|
|
|
|
|
getOclCommandKindString(command->type()), command, events.size());
|
2018-04-04 18:00:17 -04:00
|
|
|
for (const auto& it : events) {
|
2017-04-13 13:56:38 -04:00
|
|
|
// Only wait if the command is enqueued into another queue.
|
2018-04-04 18:00:17 -04:00
|
|
|
if (it->command().queue() != this) {
|
2020-06-19 13:49:43 -04:00
|
|
|
// Runtime has to flush the current batch only if the dependent wait is blocking
|
|
|
|
|
if (it->command().status() != CL_COMPLETE) {
|
2022-06-22 19:39:43 +00:00
|
|
|
ClPrint(LOG_DEBUG, LOG_CMD, "Command (%s) %p awaiting event: %p", getOclCommandKindString(command->type()), command, it);
|
2020-06-19 13:49:43 -04:00
|
|
|
virtualDevice->flush(head, true);
|
|
|
|
|
tail = head = NULL;
|
|
|
|
|
dependencyFailed |= !it->awaitCompletion();
|
|
|
|
|
}
|
2017-04-13 13:56:38 -04:00
|
|
|
}
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
// Insert the command to the linked list.
|
|
|
|
|
if (NULL == head) { // if the list is empty
|
|
|
|
|
head = tail = command;
|
|
|
|
|
} else {
|
|
|
|
|
tail->setNext(command);
|
|
|
|
|
tail = command;
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
if (dependencyFailed) {
|
|
|
|
|
command->setStatus(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2022-03-29 22:48:19 -07:00
|
|
|
ClPrint(LOG_DEBUG, LOG_CMD, "Command (%s) submitted: %p", getOclCommandKindString(command->type()), command);
|
2019-11-06 15:19:25 -05:00
|
|
|
|
2019-11-06 17:55:50 -05:00
|
|
|
command->setStatus(CL_SUBMITTED);
|
|
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
// Submit to the device queue.
|
|
|
|
|
command->submit(*virtualDevice);
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2019-10-18 17:47:30 -04:00
|
|
|
// if this is a user invisible marker command, then flush
|
|
|
|
|
if (0 == command->type()) {
|
2017-04-13 13:56:38 -04:00
|
|
|
virtualDevice->flush(head);
|
|
|
|
|
tail = head = NULL;
|
|
|
|
|
}
|
|
|
|
|
} // while (true) {
|
2014-07-04 16:17:05 -04:00
|
|
|
}
|
|
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
void HostQueue::append(Command& command) {
|
|
|
|
|
// We retain the command here. It will be released when its status
|
|
|
|
|
// changes to CL_COMPLETE
|
2019-08-19 17:39:27 -04:00
|
|
|
if ((command.getWaitBits() & 0x1) != 0) {
|
|
|
|
|
finish();
|
|
|
|
|
}
|
2017-04-13 13:56:38 -04:00
|
|
|
command.retain();
|
|
|
|
|
command.setStatus(CL_QUEUED);
|
|
|
|
|
queue_.enqueue(&command);
|
2020-07-10 08:32:15 -07:00
|
|
|
if (!IS_HIP) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-08-24 17:52:34 -07:00
|
|
|
|
2021-07-26 14:26:32 -07:00
|
|
|
// Set last submitted command
|
2022-10-21 17:49:08 -04:00
|
|
|
Command* prevLastEnqueueCommand = nullptr;
|
2023-08-24 17:36:00 +00:00
|
|
|
|
2022-10-21 17:49:08 -04:00
|
|
|
// Attach only real commands and skip internal notifications for CPU queue
|
|
|
|
|
if (command.waitingEvent() == nullptr) {
|
|
|
|
|
command.retain();
|
|
|
|
|
|
2021-07-26 14:26:32 -07:00
|
|
|
// lastCmdLock_ ensures that lastEnqueueCommand() can retain the command before it is swapped
|
|
|
|
|
// out. We want to keep this critical section as short as possible, so the command should be
|
|
|
|
|
// released outside this section.
|
|
|
|
|
ScopedLock l(lastCmdLock_);
|
2021-03-01 06:27:52 -05:00
|
|
|
|
2021-07-26 14:26:32 -07:00
|
|
|
prevLastEnqueueCommand = lastEnqueueCommand_;
|
|
|
|
|
lastEnqueueCommand_ = &command;
|
|
|
|
|
}
|
2020-08-24 17:52:34 -07:00
|
|
|
|
2021-07-26 14:26:32 -07:00
|
|
|
if (prevLastEnqueueCommand != nullptr) {
|
|
|
|
|
prevLastEnqueueCommand->release();
|
2020-07-10 08:32:15 -07:00
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
}
|
|
|
|
|
|
2018-09-06 15:21:28 -04:00
|
|
|
bool HostQueue::isEmpty() {
|
|
|
|
|
// Get a snapshot of queue size
|
|
|
|
|
return queue_.empty();
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-18 13:41:32 -04:00
|
|
|
Command* HostQueue::getLastQueuedCommand(bool retain) {
|
2020-11-26 15:49:25 -05:00
|
|
|
if (AMD_DIRECT_DISPATCH) {
|
|
|
|
|
// The batch update must be lock protected to avoid a race condition
|
|
|
|
|
// when multiple threads submit/flush/update the batch at the same time
|
2021-02-03 10:13:22 -05:00
|
|
|
ScopedLock sl(vdev()->execution());
|
2020-11-26 15:49:25 -05:00
|
|
|
// Since the lastCmdLock_ is acquired, it is safe to read and retain the lastEnqueueCommand.
|
|
|
|
|
// It is guaranteed that the pointer will not change.
|
|
|
|
|
if (retain && lastEnqueueCommand_ != nullptr) {
|
|
|
|
|
lastEnqueueCommand_->retain();
|
|
|
|
|
}
|
2020-12-10 03:23:59 -05:00
|
|
|
return lastEnqueueCommand_;
|
2020-11-26 15:49:25 -05:00
|
|
|
} else {
|
|
|
|
|
// Get last submitted command
|
|
|
|
|
ScopedLock l(lastCmdLock_);
|
2020-05-27 17:15:58 -07:00
|
|
|
|
2020-11-26 15:49:25 -05:00
|
|
|
// Since the lastCmdLock_ is acquired, it is safe to read and retain the lastEnqueueCommand.
|
|
|
|
|
// It is guaranteed that the pointer will not change.
|
|
|
|
|
if (retain && lastEnqueueCommand_ != nullptr) {
|
|
|
|
|
lastEnqueueCommand_->retain();
|
|
|
|
|
}
|
2020-12-10 03:23:59 -05:00
|
|
|
return lastEnqueueCommand_;
|
2020-05-29 11:06:55 -04:00
|
|
|
}
|
2018-10-18 13:41:32 -04:00
|
|
|
}
|
|
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
DeviceQueue::~DeviceQueue() {
|
|
|
|
|
delete virtualDevice_;
|
|
|
|
|
ScopedLock lock(context().lock());
|
|
|
|
|
context().removeDeviceQueue(device(), this);
|
2014-07-04 16:17:05 -04:00
|
|
|
}
|
|
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
bool DeviceQueue::create() {
|
|
|
|
|
const bool defaultDeviceQueue = properties().test(CL_QUEUE_ON_DEVICE_DEFAULT);
|
|
|
|
|
bool result = false;
|
|
|
|
|
|
2022-05-18 12:57:55 -04:00
|
|
|
virtualDevice_ = device().createVirtualDevice(this);
|
2017-04-13 13:56:38 -04:00
|
|
|
if (virtualDevice_ != NULL) {
|
|
|
|
|
result = true;
|
|
|
|
|
context().addDeviceQueue(device(), this, defaultDeviceQueue);
|
|
|
|
|
}
|
2014-07-04 16:17:05 -04:00
|
|
|
|
2017-04-13 13:56:38 -04:00
|
|
|
return result;
|
2014-07-04 16:17:05 -04:00
|
|
|
}
|
|
|
|
|
|
2019-08-19 17:39:27 -04:00
|
|
|
} // namespace amd
|