blob: 470d0bf1f75b5587278afd402cef59e67b07049c [file] [log] [blame]
// Copyright 2019 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.
#include "buffer_collection.h"
#include <fuchsia/sysmem/c/fidl.h>
#include <lib/fidl-async-2/fidl_struct.h>
#include <lib/fidl-utils/bind.h>
#include <lib/sysmem-version/sysmem-version.h>
#include <zircon/compiler.h>
#include <zircon/errors.h>
#include <atomic>
#include <ddk/trace/event.h>
#include "fuchsia/sysmem/c/fidl.h"
#include "logical_buffer_collection.h"
#include "macros.h"
#include "utils.h"
namespace sysmem_driver {
namespace {
using BufferCollectionInfo_v1 = FidlStruct<fuchsia_sysmem_BufferCollectionInfo_2,
llcpp::fuchsia::sysmem::BufferCollectionInfo_2>;
constexpr uint32_t kConcurrencyCap = 64;
// For max client vmo rights, we specify the RIGHT bits individually to avoid
// picking up any newly-added rights unintentionally. This is based on
// ZX_DEFAULT_VMO_RIGHTS, but with a few rights removed.
const uint32_t kMaxClientVmoRights =
// ZX_RIGHTS_BASIC, except ZX_RIGHT_INSPECT (at least for now).
ZX_RIGHT_TRANSFER | ZX_RIGHT_DUPLICATE | ZX_RIGHT_WAIT |
// ZX_RIGHTS_IO:
ZX_RIGHT_READ | ZX_RIGHT_WRITE |
// ZX_RIGHTS_PROPERTY allows a participant to set ZX_PROP_NAME for easier
// memory metrics. Nothing prevents participants from figting over the
// name, though the kernel should make each set/get atomic with respect to
// other set/get. This relies on ZX_RIGHTS_PROPERTY not implying anything
// that could be used as an attack vector between processes sharing a VMO.
ZX_RIGHTS_PROPERTY |
// We intentionally omit ZX_RIGHT_EXECUTE (indefinitely) and ZX_RIGHT_SIGNAL
// (at least for now).
//
// Remaining bits of ZX_DEFAULT_VMO_RIGHTS (as of this writing):
ZX_RIGHT_MAP;
} // namespace
const fuchsia_sysmem_BufferCollection_ops_t BufferCollection::kOps = {
fidl::Binder<BufferCollection>::BindMember<&BufferCollection::SetEventSink>,
fidl::Binder<BufferCollection>::BindMember<&BufferCollection::Sync>,
fidl::Binder<BufferCollection>::BindMember<&BufferCollection::SetConstraints>,
fidl::Binder<BufferCollection>::BindMember<&BufferCollection::WaitForBuffersAllocated>,
fidl::Binder<BufferCollection>::BindMember<&BufferCollection::CheckBuffersAllocated>,
fidl::Binder<BufferCollection>::BindMember<&BufferCollection::CloseSingleBuffer>,
fidl::Binder<BufferCollection>::BindMember<&BufferCollection::AllocateSingleBuffer>,
fidl::Binder<BufferCollection>::BindMember<&BufferCollection::WaitForSingleBufferAllocated>,
fidl::Binder<BufferCollection>::BindMember<&BufferCollection::CheckSingleBufferAllocated>,
fidl::Binder<BufferCollection>::BindMember<&BufferCollection::Close>,
fidl::Binder<BufferCollection>::BindMember<&BufferCollection::SetName>,
fidl::Binder<BufferCollection>::BindMember<&BufferCollection::SetDebugClientInfo>,
fidl::Binder<BufferCollection>::BindMember<&BufferCollection::SetConstraintsAuxBuffers>,
fidl::Binder<BufferCollection>::BindMember<&BufferCollection::GetAuxBuffers>,
};
BufferCollection::~BufferCollection() {
TRACE_DURATION("gfx", "BufferCollection::~BufferCollection", "this", this, "parent",
parent_.get());
// Close() the SimpleBinding<> before deleting the list of pending Txn(s),
// so that ~Txn doesn't complain about being deleted without being
// completed.
//
// Don't run the error handler; if any error handler remains it'll just get
// deleted here.
(void)binding_.Close();
}
zx_status_t BufferCollection::SetEventSink(zx_handle_t buffer_collection_events_client_param) {
zx::channel buffer_collection_events_client(buffer_collection_events_client_param);
if (is_done_) {
FailAsync(ZX_ERR_BAD_STATE, "BufferCollectionToken::SetEventSink() when already is_done_");
// We're failing async - no need to try to fail sync.
return ZX_OK;
}
if (!buffer_collection_events_client) {
zx_status_t status = ZX_ERR_INVALID_ARGS;
FailAsync(status,
"BufferCollection::SetEventSink() must be called "
"with a non-zero handle.");
return status;
}
if (is_set_constraints_seen_) {
zx_status_t status = ZX_ERR_INVALID_ARGS;
// It's not required to use SetEventSink(), but if it's used, it must be
// before SetConstraints().
FailAsync(status,
"BufferCollection::SetEventSink() (if any) must be "
"before SetConstraints().");
return status;
}
if (events_) {
zx_status_t status = ZX_ERR_INVALID_ARGS;
FailAsync(status, "BufferCollection::SetEventSink() may only be called at most once.");
return status;
}
events_ = std::move(buffer_collection_events_client);
// We don't create BufferCollection until after processing all inbound
// messages that were queued toward the server on the token channel of the
// token that was used to create this BufferCollection, so we can send this
// event now, as all the Duplicate() inbound from that token are done being
// processed before here.
fuchsia_sysmem_BufferCollectionEventsOnDuplicatedTokensKnownByServer(events_.get());
return ZX_OK;
}
zx_status_t BufferCollection::Sync(fidl_txn_t* txn) {
BindingType::Txn::RecognizeTxn(txn);
return fuchsia_sysmem_BufferCollectionSync_reply(txn);
}
zx_status_t BufferCollection::SetConstraintsAuxBuffers(
const fuchsia_sysmem_BufferCollectionConstraintsAuxBuffers* constraints_param) {
ZX_DEBUG_ASSERT(constraints_param);
// Copy data and own handles.
static_assert(sizeof(llcpp::fuchsia::sysmem::BufferCollectionConstraintsAuxBuffers) ==
sizeof(fuchsia_sysmem_BufferCollectionConstraintsAuxBuffers));
llcpp::fuchsia::sysmem::BufferCollectionConstraintsAuxBuffers local_constraints;
memcpy(&local_constraints, constraints_param, sizeof(local_constraints));
if (is_set_constraints_aux_buffers_seen_) {
FailAsync(ZX_ERR_NOT_SUPPORTED, "SetConstraintsAuxBuffers() can be called only once.");
return ZX_ERR_NOT_SUPPORTED;
}
is_set_constraints_aux_buffers_seen_ = true;
if (is_done_) {
FailAsync(ZX_ERR_BAD_STATE,
"BufferCollectionToken::SetConstraintsAuxBuffers() when already is_done_");
// We're failing async - no need to try to fail sync.
return ZX_OK;
}
if (is_set_constraints_seen_) {
FailAsync(ZX_ERR_NOT_SUPPORTED,
"SetConstraintsAuxBuffers() after SetConstraints() causes failure.");
return ZX_ERR_NOT_SUPPORTED;
}
ZX_DEBUG_ASSERT(!constraints_aux_buffers_);
constraints_aux_buffers_.emplace(std::move(local_constraints));
// LogicalBufferCollection doesn't care about "clear aux buffers" constraints until the last
// SetConstraints(), so done for now.
return ZX_OK;
}
zx_status_t BufferCollection::SetConstraints(
bool has_constraints, const fuchsia_sysmem_BufferCollectionConstraints* constraints_param) {
TRACE_DURATION("gfx", "BufferCollection::SetConstraints", "this", this, "parent", parent_.get());
// Regardless of has_constraints or not, we need to unconditionally take
// ownership of any handles in constraints_param. Not that there are
// necessarily any handles in here currently, but to avoid being fragile re.
// any handles potentially added later.
ZX_DEBUG_ASSERT(constraints_param);
// Copy data and own handles.
static_assert(sizeof(llcpp::fuchsia::sysmem::BufferCollectionConstraints) ==
sizeof(fuchsia_sysmem_BufferCollectionConstraints));
std::optional<llcpp::fuchsia::sysmem::BufferCollectionConstraints> local_constraints;
local_constraints.emplace();
memcpy(&local_constraints.value(), constraints_param, sizeof(local_constraints.value()));
if (is_set_constraints_seen_) {
FailAsync(ZX_ERR_NOT_SUPPORTED, "2nd SetConstraints() causes failure.");
return ZX_ERR_NOT_SUPPORTED;
}
is_set_constraints_seen_ = true;
if (is_done_) {
FailAsync(ZX_ERR_BAD_STATE, "BufferCollectionToken::SetConstraints() when already is_done_");
// We're failing async - no need to try to fail sync.
return ZX_OK;
}
if (!has_constraints) {
// Not needed.
local_constraints.reset();
if (is_set_constraints_aux_buffers_seen_) {
// No constraints are fine, just not aux buffers constraints without main constraints, because
// I can't think of any reason why we'd need to support aux buffers constraints without main
// constraints, so disallow at least for now.
FailAsync(ZX_ERR_NOT_SUPPORTED, "SetConstraintsAuxBuffers() && !has_constraints");
return ZX_ERR_NOT_SUPPORTED;
}
}
ZX_DEBUG_ASSERT(!constraints_);
// enforced above
ZX_DEBUG_ASSERT(!constraints_aux_buffers_ || local_constraints);
ZX_DEBUG_ASSERT(has_constraints == !!local_constraints);
{ // scope result
auto result = sysmem::V2CopyFromV1BufferCollectionConstraints(
&allocator_, local_constraints ? &local_constraints.value() : nullptr,
constraints_aux_buffers_ ? &constraints_aux_buffers_.value() : nullptr);
if (!result.is_ok()) {
parent_->LogClientError(&debug_info_, "V2CopyFromV1BufferCollectionConstraints() failed");
return ZX_ERR_INVALID_ARGS;
}
ZX_DEBUG_ASSERT(!result.value().IsEmpty() || !local_constraints);
constraints_.emplace(result.take_value());
} // ~result
// No longer needed.
constraints_aux_buffers_.reset();
// Stash BufferUsage also, for benefit of GetUsageBasedRightsAttenuation() depsite
// TakeConstraints().
{ // scope buffer_usage
llcpp::fuchsia::sysmem::BufferUsage empty_buffer_usage{};
llcpp::fuchsia::sysmem::BufferUsage* source_buffer_usage = &empty_buffer_usage;
if (local_constraints) {
source_buffer_usage = &local_constraints.value().usage;
}
auto result = sysmem::V2CopyFromV1BufferUsage(&allocator_, *source_buffer_usage);
if (!result.is_ok()) {
parent_->LogClientError(&debug_info_, "V2CopyFromV1BufferUsage failed");
// Not expected given current impl of sysmem-version.
return ZX_ERR_INTERNAL;
}
usage_.emplace(result.take_value().build());
} // ~buffer_usage
// LogicalBufferCollection will ask for constraints when it needs them,
// possibly during this call if this is the last participant to report
// having initial constraints.
//
// The LogicalBufferCollection cares if this BufferCollection view has null
// constraints, but only later when it asks for the specific constraints.
parent()->OnSetConstraints();
// |this| may be gone at this point, if the allocation failed. Regardless,
// SetConstraints() worked, so ZX_OK.
return ZX_OK;
}
zx_status_t BufferCollection::WaitForBuffersAllocated(fidl_txn_t* txn_param) {
TRACE_DURATION("gfx", "BufferCollection::WaitForBuffersAllocated", "this", this, "parent",
parent_.get());
BindingType::Txn::RecognizeTxn(txn_param);
if (is_done_) {
FailAsync(ZX_ERR_BAD_STATE,
"BufferCollectionToken::WaitForBuffersAllocated() when already is_done_");
// We're failing async - no need to fail sync.
return ZX_OK;
}
// In general we're handling this async, so take ownership of the txn.
std::unique_ptr<BindingType::Txn> txn = BindingType::Txn::TakeTxn(txn_param);
trace_async_id_t current_event_id = TRACE_NONCE();
TRACE_ASYNC_BEGIN("gfx", "BufferCollection::WaitForBuffersAllocated async", current_event_id,
"this", this, "parent", parent_.get());
pending_wait_for_buffers_allocated_.emplace_back(
std::make_pair(current_event_id, std::move(txn)));
// The allocation is a one-shot (once true, remains true) and may already be
// done, in which case this immediately completes txn.
MaybeCompleteWaitForBuffersAllocated();
return ZX_OK;
}
zx_status_t BufferCollection::CheckBuffersAllocated(fidl_txn_t* txn) {
BindingType::Txn::RecognizeTxn(txn);
if (is_done_) {
FailAsync(ZX_ERR_BAD_STATE,
"BufferCollectionToken::CheckBuffersAllocated() when "
"already is_done_");
// We're failing async - no need to try to fail sync.
return ZX_OK;
}
LogicalBufferCollection::AllocationResult allocation_result = parent()->allocation_result();
if (allocation_result.status == ZX_OK && !allocation_result.buffer_collection_info) {
return fuchsia_sysmem_BufferCollectionCheckBuffersAllocated_reply(txn, ZX_ERR_UNAVAILABLE);
}
// Buffer collection has either been allocated or failed.
return fuchsia_sysmem_BufferCollectionCheckBuffersAllocated_reply(txn, allocation_result.status);
}
zx_status_t BufferCollection::GetAuxBuffers(fidl_txn_t* txn_param) {
BindingType::Txn::RecognizeTxn(txn_param);
LogicalBufferCollection::AllocationResult allocation_result = parent()->allocation_result();
if (allocation_result.status == ZX_OK && !allocation_result.buffer_collection_info) {
FailAsync(ZX_ERR_BAD_STATE, "GetAuxBuffers() called before allocation complete.",
ZX_ERR_BAD_STATE);
// We're failing async - no need to fail sync.
return ZX_OK;
}
if (allocation_result.status != ZX_OK) {
FailAsync(ZX_ERR_BAD_STATE, "GetAuxBuffers() called after allocation failure.",
ZX_ERR_BAD_STATE);
// We're failing async - no need to fail sync.
return ZX_OK;
}
BufferCollection::V1CBufferCollectionInfo v1_c(
BufferCollection::V1CBufferCollectionInfo::Default);
ZX_DEBUG_ASSERT(allocation_result.buffer_collection_info);
auto v1_c_result = CloneAuxBuffersResultForSendingV1(*allocation_result.buffer_collection_info);
if (!v1_c_result.is_ok()) {
// FailAsync() already called.
//
// We're failing async - no need to fail sync.
return ZX_OK;
}
v1_c = v1_c_result.take_value();
// Ownership of handles in to_send are transferred to _reply().
zx_status_t reply_status = fuchsia_sysmem_BufferCollectionGetAuxBuffers_reply(
txn_param, allocation_result.status, v1_c.release());
if (reply_status != ZX_OK) {
FailAsync(reply_status,
"fuchsia_sysmem_BufferCollectionGetAuxBuffers_reply failed - status: %d",
reply_status);
// We're failing async - no need to fail sync.
return ZX_OK;
}
return ZX_OK;
}
zx_status_t BufferCollection::CloseSingleBuffer(uint64_t buffer_index) {
if (is_done_) {
FailAsync(ZX_ERR_BAD_STATE, "BufferCollectionToken::CloseSingleBuffer() when already is_done_");
// We're failing async - no need to try to fail sync.
return ZX_OK;
}
// FailAsync() instead of returning a failure, mainly because FailAsync()
// prints a message that's more obvious than the generic _dispatch() failure
// would.
FailAsync(ZX_ERR_NOT_SUPPORTED, "CloseSingleBuffer() not yet implemented");
return ZX_OK;
}
zx_status_t BufferCollection::AllocateSingleBuffer(uint64_t buffer_index) {
if (is_done_) {
FailAsync(ZX_ERR_BAD_STATE,
"BufferCollectionToken::AllocateSingleBuffer() when already "
"is_done_");
// We're failing async - no need to try to fail sync.
return ZX_OK;
}
FailAsync(ZX_ERR_NOT_SUPPORTED, "AllocateSingleBuffer() not yet implemented");
return ZX_OK;
}
zx_status_t BufferCollection::WaitForSingleBufferAllocated(uint64_t buffer_index, fidl_txn_t* txn) {
BindingType::Txn::RecognizeTxn(txn);
if (is_done_) {
FailAsync(ZX_ERR_BAD_STATE,
"BufferCollectionToken::WaitForSingleBufferAllocated() when "
"already is_done_");
// We're failing async - no need to try to fail sync.
return ZX_OK;
}
FailAsync(ZX_ERR_NOT_SUPPORTED, "WaitForSingleBufferAllocated() not yet implemented");
return ZX_OK;
}
zx_status_t BufferCollection::CheckSingleBufferAllocated(uint64_t buffer_index) {
if (is_done_) {
FailAsync(ZX_ERR_BAD_STATE,
"BufferCollectionToken::CheckSingleBufferAllocated() when "
"already is_done_");
// We're failing async - no need to try to fail sync.
return ZX_OK;
}
FailAsync(ZX_ERR_NOT_SUPPORTED, "CheckSingleBufferAllocated() not yet implemented");
return ZX_OK;
}
zx_status_t BufferCollection::Close() {
if (is_done_) {
FailAsync(ZX_ERR_BAD_STATE, "BufferCollection::Close() when already closed.");
return ZX_OK;
}
// We still want to enforce that the client doesn't send any other messages
// between Close() and closing the channel, so we just set is_done_ here and
// do a FailAsync() if is_done_ is seen to be set while handling any other
// message.
is_done_ = true;
return ZX_OK;
}
zx_status_t BufferCollection::SetName(uint32_t priority, const char* name_data, size_t name_size) {
parent_->SetName(priority, std::string(name_data, name_size));
return ZX_OK;
}
zx_status_t BufferCollection::SetDebugClientInfo(const char* name_data, size_t name_size,
uint64_t id) {
debug_info_.name = std::string(name_data, name_size);
debug_info_.id = id;
debug_id_property_ = node_.CreateUint("debug_id", debug_info_.id);
debug_name_property_ = node_.CreateString("debug_name", debug_info_.name);
return ZX_OK;
}
void BufferCollection::FailAsync(zx_status_t status, const char* format, ...) {
va_list args;
va_start(args, format);
parent_->VLogClientError(&debug_info_, format, args);
va_end(args);
FidlServer::FailAsync(status, "");
}
fit::result<llcpp::fuchsia::sysmem2::BufferCollectionInfo::Builder>
BufferCollection::CloneResultForSendingV2(
const llcpp::fuchsia::sysmem2::BufferCollectionInfo& buffer_collection_info) {
auto clone_result = sysmem::V2CloneBufferCollectionInfo(
&allocator_, buffer_collection_info, GetClientVmoRights(), GetClientAuxVmoRights());
if (!clone_result.is_ok()) {
FailAsync(clone_result.error(),
"CloneResultForSendingV1() V2CloneBufferCollectionInfo() failed - status: %d",
clone_result.error());
return fit::error();
}
auto v2_b = clone_result.take_value();
if (!IsAnyUsage(usage_.value())) {
// No VMO handles should be sent to the client in this case.
if (v2_b.has_buffers()) {
for (auto& vmo_buffer : v2_b.buffers()) {
if (vmo_buffer.has_vmo()) {
vmo_buffer.vmo().reset();
}
if (vmo_buffer.has_aux_vmo()) {
vmo_buffer.aux_vmo().reset();
}
}
}
}
return fit::ok(std::move(v2_b));
}
fit::result<BufferCollection::V1CBufferCollectionInfo> BufferCollection::CloneResultForSendingV1(
const llcpp::fuchsia::sysmem2::BufferCollectionInfo& buffer_collection_info) {
auto v2_builder_result = CloneResultForSendingV2(buffer_collection_info);
if (!v2_builder_result.is_ok()) {
// FailAsync() already called.
return fit::error();
}
auto v1_result = sysmem::V1MoveFromV2BufferCollectionInfo(v2_builder_result.take_value().build());
if (!v1_result.is_ok()) {
FailAsync(ZX_ERR_INVALID_ARGS,
"CloneResultForSendingV1() V1MoveFromV2BufferCollectionInfo() failed");
return fit::error();
}
V1CBufferCollectionInfo v1_c(V1CBufferCollectionInfo::Default);
// struct move
v1_c.BorrowAsLlcpp() = v1_result.take_value();
return fit::ok(std::move(v1_c));
}
fit::result<BufferCollection::V1CBufferCollectionInfo>
BufferCollection::CloneAuxBuffersResultForSendingV1(
const llcpp::fuchsia::sysmem2::BufferCollectionInfo& buffer_collection_info) {
auto v2_builder_result = CloneResultForSendingV2(buffer_collection_info);
if (!v2_builder_result.is_ok()) {
// FailAsync() already called.
return fit::error();
}
auto v1_result =
sysmem::V1AuxBuffersMoveFromV2BufferCollectionInfo(v2_builder_result.take_value().build());
if (!v1_result.is_ok()) {
FailAsync(ZX_ERR_INVALID_ARGS,
"CloneResultForSendingV1() V1MoveFromV2BufferCollectionInfo() failed");
return fit::error();
}
V1CBufferCollectionInfo v1_c(V1CBufferCollectionInfo::Default);
// struct move
v1_c.BorrowAsLlcpp() = v1_result.take_value();
return fit::ok(std::move(v1_c));
}
void BufferCollection::OnBuffersAllocated() {
// Any that are pending are completed by this call or something called
// FailAsync(). It's fine for this method to ignore the fact that
// FailAsync() may have already been called. That's essentially the main
// reason we have FailAsync() instead of Fail().
MaybeCompleteWaitForBuffersAllocated();
if (!events_) {
return;
}
BufferCollection::V1CBufferCollectionInfo v1_c(
BufferCollection::V1CBufferCollectionInfo::Default);
if (parent()->allocation_result().status == ZX_OK) {
ZX_DEBUG_ASSERT(parent()->allocation_result().buffer_collection_info);
auto v1_c_result =
CloneResultForSendingV1(*parent()->allocation_result().buffer_collection_info);
if (!v1_c_result.is_ok()) {
// FailAsync() already called.
return;
}
v1_c = v1_c_result.take_value();
}
// Ownership of all handles in to_send is transferred to this function.
fuchsia_sysmem_BufferCollectionEventsOnBuffersAllocated(
events_.get(), parent()->allocation_result().status, v1_c.release());
}
bool BufferCollection::has_constraints() { return !!constraints_; }
const llcpp::fuchsia::sysmem2::BufferCollectionConstraints::Builder&
BufferCollection::constraints() {
ZX_DEBUG_ASSERT(has_constraints());
return constraints_.value();
}
llcpp::fuchsia::sysmem2::BufferCollectionConstraints::Builder BufferCollection::TakeConstraints() {
ZX_DEBUG_ASSERT(has_constraints());
llcpp::fuchsia::sysmem2::BufferCollectionConstraints::Builder result =
std::move(constraints_.value());
constraints_.reset();
return result;
}
LogicalBufferCollection* BufferCollection::parent() { return parent_.get(); }
fbl::RefPtr<LogicalBufferCollection> BufferCollection::parent_shared() { return parent_; }
bool BufferCollection::is_done() { return is_done_; }
BufferCollection::BufferCollection(fbl::RefPtr<LogicalBufferCollection> parent)
: FidlServer(parent->parent_device()->dispatcher(), "BufferCollection", kConcurrencyCap),
parent_(parent),
allocator_(parent->fidl_allocator()) {
TRACE_DURATION("gfx", "BufferCollection::BufferCollection", "this", this, "parent",
parent_.get());
ZX_DEBUG_ASSERT(parent_);
node_ = parent_->node().CreateChild(CreateUniqueName("collection-"));
}
// This method is only meant to be called from GetClientVmoRights().
uint32_t BufferCollection::GetUsageBasedRightsAttenuation() {
// This method won't be called for participants without any buffer data "usage".
ZX_DEBUG_ASSERT(usage_);
// We assume that read and map are both needed by all participants with any "usage". Only
// ZX_RIGHT_WRITE is controlled by usage.
// It's not this method's job to attenuate down to kMaxClientVmoRights, so
// let's not pretend like it is.
uint32_t result = std::numeric_limits<uint32_t>::max();
if (!IsWriteUsage(usage_.value())) {
result &= ~ZX_RIGHT_WRITE;
}
return result;
}
uint32_t BufferCollection::GetClientVmoRights() {
return
// max possible rights for a client to have
kMaxClientVmoRights &
// attenuate write if client doesn't need write
GetUsageBasedRightsAttenuation() &
// attenuate according to BufferCollectionToken.Duplicate() rights
// parameter so that initiator and participant can distribute the token
// and remove any unnecessary/unintended rights along the way.
client_rights_attenuation_mask_;
}
uint32_t BufferCollection::GetClientAuxVmoRights() {
// At least for now.
return GetClientVmoRights();
}
void BufferCollection::MaybeCompleteWaitForBuffersAllocated() {
LogicalBufferCollection::AllocationResult allocation_result = parent()->allocation_result();
if (allocation_result.status == ZX_OK && !allocation_result.buffer_collection_info) {
// Everything is ok so far, but allocation isn't done yet.
return;
}
while (!pending_wait_for_buffers_allocated_.empty()) {
auto [async_id, txn] = std::move(pending_wait_for_buffers_allocated_.front());
pending_wait_for_buffers_allocated_.pop_front();
BufferCollection::V1CBufferCollectionInfo v1_c(
BufferCollection::V1CBufferCollectionInfo::Default);
if (allocation_result.status == ZX_OK) {
ZX_DEBUG_ASSERT(allocation_result.buffer_collection_info);
auto v1_c_result = CloneResultForSendingV1(*allocation_result.buffer_collection_info);
if (!v1_c_result.is_ok()) {
// FailAsync() already called.
return;
}
v1_c = v1_c_result.take_value();
}
TRACE_ASYNC_END("gfx", "BufferCollection::WaitForBuffersAllocated async", async_id, "this",
this, "parent", parent_.get());
// Ownership of handles in to_send are transferred to _reply().
zx_status_t reply_status = fuchsia_sysmem_BufferCollectionWaitForBuffersAllocated_reply(
&txn->raw_txn(), allocation_result.status, v1_c.release());
if (reply_status != ZX_OK) {
FailAsync(reply_status,
"fuchsia_sysmem_BufferCollectionWaitForBuffersAllocated_"
"reply failed - status: %d",
reply_status);
return;
}
// ~txn
}
}
} // namespace sysmem_driver