blob: 50837eb707b9c428376000b47e16fff291bb8a23 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef INFLIGHT_LIST_H
#define INFLIGHT_LIST_H
#include "magma.h"
#include "magma_util/dlog.h"
#include "magma_util/macros.h"
#include <deque>
#include <errno.h>
#include <poll.h>
#include <unistd.h>
namespace magma {
// A convenience class for maintaining a list of inflight command buffers,
// by reading completed buffer ids from the notification channel.
// Caution, this approach only works for drivers that report completions
// in this format.
// Note, this class is not threadsafe.
class InflightList {
public:
InflightList(magma_connection_t connection) : fd_(magma_get_notification_channel_fd(connection))
{
DASSERT(fd_ >= 0);
}
~InflightList() { Shutdown(); }
void add(uint64_t buffer_id) { buffers_.push_back(buffer_id); }
void release(uint64_t buffer_id)
{
auto iter = std::find(buffers_.begin(), buffers_.end(), buffer_id);
DASSERT(iter == buffers_.begin());
buffers_.erase(iter);
}
uint32_t size() { return buffers_.size(); }
bool is_inflight(uint64_t buffer_id)
{
return std::find(buffers_.begin(), buffers_.end(), buffer_id) != buffers_.end();
}
// Wait on the fd for a completion; returns true if a completion was
// received before |timeout_ms|.
bool WaitForCompletion(uint64_t timeout_ms)
{
if (fd_ < 0)
return DRETF(false, "notification fd has been closed");
pollfd pfd = {};
pfd.events = POLLIN;
pfd.fd = fd_;
int status = poll(&pfd, 1, timeout_ms == UINT64_MAX ? -1 : timeout_ms);
if (status < 0)
DLOG("poll returned status %d errno %d", status, errno);
return status == 1;
}
// Read all outstanding completions and update the inflight list.
void ServiceCompletions(magma_connection_t connection)
{
uint64_t buffer_ids[8];
uint64_t bytes_available = 0;
while (true) {
magma_status_t status = magma_read_notification_channel(
connection, buffer_ids, sizeof(buffer_ids), &bytes_available);
if (status != MAGMA_STATUS_OK) {
DLOG("magma_read_notification_channel returned %d", status);
Shutdown();
return;
}
if (bytes_available == 0)
return;
DASSERT(bytes_available % sizeof(uint64_t) == 0);
for (uint32_t i = 0; i < bytes_available / sizeof(uint64_t); i++) {
DASSERT(is_inflight(buffer_ids[i]));
release(buffer_ids[i]);
}
}
}
private:
void Shutdown()
{
if (fd_ >= 0) {
close(fd_);
fd_ = -1;
}
}
std::deque<uint64_t> buffers_;
int32_t fd_;
};
} // namespace magma
#endif // INFLIGHT_LIST_H