blob: 352f48b02e4be44e8f1a62018037478a07faf1a8 [file] [log] [blame]
// Copyright 2021 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 SRC_DEVICES_SYSMEM_DRIVERS_SYSMEM_NODE_PROPERTIES_H_
#define SRC_DEVICES_SYSMEM_DRIVERS_SYSMEM_NODE_PROPERTIES_H_
#include <fuchsia/sysmem2/llcpp/fidl.h>
#include <lib/fit/function.h>
#include <stdint.h>
#include <memory>
#include <unordered_map>
#include "table_holder.h"
#include "logging.h"
namespace sysmem_driver {
// ClientDebugInfo carries debug-specific information that can be attached to a Node, either by a
// participant, or using values inherited from the parent Node, or default values established when
// the root Node is created.
struct ClientDebugInfo {
std::string name;
zx_koid_t id{};
};
// ErrorPropagationMode
//
// The ErrorPropagationMode controls propagation of failure up the Node tree (failures always
// propagate down the Node tree), and also controls how much of the Node tree is involved in initial
// allocation, and also how much of the Node tree is involved in subsequent logical allocations.
// The granularity of subsequent logical allocations is designed to mimic the behavior of initial
// allocation, so that a given Node/participant connection sees the same allocation granularity
// with respect to SetDispensable() or AttachToken() sub-trees regardless of whether the Node itself
// is involved in initial allocation or a later logical allocation.
//
// The ErrorPropagationMode of a BufferCollectionToken / BufferCollection doesn't imply anything
// about the ErrorPropagationMode of its parent or children.
//
// SetDispensable() results in kPropagateBeforeAllocation.
//
// AttachToken() results in kDoNotPropagate.
//
// Failure of a BufferCollectionToken / BufferCollection will fail all its children, and will fail
// its immediate parent if ErrorPropagationMode is kPropagate or if ErrorPropagationMode is
// kPropagateBeforeAllocation and allocation (or logical allocation) has not yet occurred.
//
// Initial allocation will aggregate constraints of all nodes from the root down, with the exception
// of any sub-trees rooted at a kDoNotPropagate node.
//
// A sub-tree rooted at a kDoNotPropagate node will not aggregate its constraints into initial
// allocation.
//
// A sub-tree rooted at a kDoNotPropagate node, with a further sub-tree that's kDoNotPropagate, will
// separately aggregate the parent portion and succeed or fail logical allocation of that portion,
// then separately aggregate the kDoNotPropagate sub-tree and succeed or fail that portion. This
// maximizes behavior simimlarity between a root with a kDoNotPropagate sub-tree and a
// kDoNotPropagate sub-tree with a further kDoNotPropagate sub-tree.
enum class ErrorPropagationMode : uint32_t {
// On child failure, always fail the parent. This is the mode of a token created via
// fuchsia.sysmem.Allocator.AllocateSharedCollection() (the root), and the initial mode of a token
// created via fuchsia.sysmem.BufferCollectionToken.Duplicate().
kPropagate,
// On child failure, fail the parent only if initial allocation has not yet occurred. This is the
// mode of a token after SetDispensable() on that token (unless the token was already
// kDoNotPropagate, in which case it's still kDoNotPropagate).
kPropagateBeforeAllocation,
// Never fail the parent. This is the mode of a token created with AttachToken().
kDoNotPropagate,
};
struct NodeFilterResult {
bool keep_node = true;
bool iterate_children = true;
};
class Node;
class LogicalBufferCollection;
// The NodeProperties are properties that are not specific to whether the node is presently a
// live BufferCollectionToken, live BufferCollection, or just a raw non-live NodeProperties in
// orphaned_constraints_.
//
// This struct stays allocated as a BufferCollectionToken changes into a BufferCollection. The
// node pointer is updated during that conversion, as the TreeNode interface is implemented by
// BufferCollectionToken and BufferCollection separately.
//
// Things that can change when transmuting from BufferCollectionToken to BufferCollection, from
// BufferCollectionToken to OrphanedNode, or from BufferCollection to OrphanedNode, should generally
// go in Node. Things that don't change when transmuting go in NodeProperties.
class NodeProperties {
public:
// We keep pointers to NodeProperties around, so no copying or moving.
NodeProperties(const NodeProperties& to_copy) = delete;
NodeProperties(NodeProperties&& to_move) = delete;
~NodeProperties();
// These are the only ways for client code to create a new NodeProperties. These enforce that
// NodeProperties are to be lifetime-managed using std::unique_ptr<NodeProperties>. This is part
// of preserving linkages from child NodeProperties to parent NodeProperties using a
// NodeProperties*, since the child Node existing doesn't keep the parent alive.
static std::unique_ptr<NodeProperties> NewRoot(
LogicalBufferCollection* logical_buffer_collection);
// The returned NodeProperties is already linked into the tree, and owned by the tree, so this
// method just returns a raw pointer so we can inform the Node of its NodeProperties.
NodeProperties* NewChild(LogicalBufferCollection* logical_buffer_collection);
// Only for LogicalBufferCollection to use for temporary internal constraints. We still enforce
// that all instances of NodeProperties are managed by std::unique_ptr<NodeProperties> for
// consistency.
static std::unique_ptr<NodeProperties> NewTemporary(
LogicalBufferCollection* logical_buffer_collection,
fuchsia_sysmem2::wire::BufferCollectionConstraints buffer_collection_constraints,
std::string debug_name);
// Remove this NodeProperties from the tree by unlinking this NodeProperties from its parent,
// which in turn will delete this NodeProperties, and also delete the corresponding Node.
//
// This call requires that this NodeProperties has zero children.
void RemoveFromTreeAndDelete();
// With default parameters, this returns a list of all the TreeNodeLinkage(s) starting at this
// node as root, in breadth-first order, which can be used to Fail() all the nodes including this
// node, by working from the back to the front of the list. This breadth-first order is generated
// without stack recursion, and Fail() from back to front of the returned vector also doesn't
// involve stack recursion.
//
// If a node_filter is provided, and returns false for a given node, that node and the children of
// that node are skipped.
//
// The default node_filter matches all nodes.
std::vector<NodeProperties*> BreadthFirstOrder(
fit::function<NodeFilterResult(const NodeProperties&)> node_filter =
fit::function<NodeFilterResult(const NodeProperties&)>());
NodeProperties* parent() const;
Node* node() const;
uint32_t child_count() const;
ClientDebugInfo& client_debug_info();
const ClientDebugInfo& client_debug_info() const;
uint32_t& rights_attenuation_mask();
ErrorPropagationMode& error_propagation_mode();
const ErrorPropagationMode& error_propagation_mode() const;
bool buffers_logically_allocated() const;
void SetBuffersLogicallyAllocated();
// BufferCollectionToken never has constraints yet, so returns nullptr.
// BufferCollection may have constraints.
// OrphanedConstraints may have constraints.
bool has_constraints() const;
const fuchsia_sysmem2::wire::BufferCollectionConstraints* buffer_collection_constraints() const;
void SetBufferCollectionConstraints(
TableHolder<fuchsia_sysmem2::wire::BufferCollectionConstraints>
buffer_collection_constraints);
void SetNode(fbl::RefPtr<Node> node);
// These counts are for the current NodeProperties + any current children of the current
// NodeProperties. For LogicalBufferCollection::root_, these counts are for the whole tree.
//
// TODO(fxbug.dev/71454): Limit node_count() of root_, but instead of failing root_ when limit
// reached, prune a sub-tree selected to prefer more-nested over less nested, and larger node
// count over smaller node count (lexicographically).
uint32_t node_count() const;
uint32_t connected_client_count() const;
uint32_t buffer_collection_count() const;
uint32_t buffer_collection_token_count() const;
void LogInfo(Location location, const char* format, ...) const __PRINTFLIKE(3, 4);
// For debugging.
void LogConstraints(Location location);
private:
friend class LogicalBufferCollection;
explicit NodeProperties(LogicalBufferCollection* logical_buffer_collection);
LogicalBufferCollection* logical_buffer_collection_ = nullptr;
// Node linkage.
//
// The node field is updated when a BufferCollectionToken is transformed into a BufferCollection,
// and when/if a BufferCollection is transformed into an OrphanedNode.
//
// In contrast, any pointers to the NodeProperties structure (such as from child to parent) do not
// need to be updated, because NodeProperties is allocated separately from the Node itself, and
// NodeProperties doesn't deallocate or move when the Node changes from one type to another.
NodeProperties* parent_ = nullptr;
fbl::RefPtr<Node> node_;
// We use shared_ptr<> instead of unique_ptr<> here only so that Node can keep a std::weak_ptr<>.
// The only non-transient ownership of NodeProperties is by the tree at
// LogicalBufferCollection::root_.
std::unordered_map<NodeProperties*, std::shared_ptr<NodeProperties>> children_;
ClientDebugInfo client_debug_info_{};
// The rights attenuation mask driven by BufferCollectionToken::Duplicate()
// rights_attenuation_mask parameter(s) as the token is duplicated,
// potentially via multiple participants.
//
// 1 bit means the right is allowed. 0 bit means the right is attenuated.
uint32_t rights_attenuation_mask_ = std::numeric_limits<uint32_t>::max();
// In the absence of SetDispensable() and AttachToken(), only kPropagate mode is used.
//
// SetDispensable() results in kPropagateBeforeAllocation.
//
// AttachToken() results in kDoNotPropagate.
ErrorPropagationMode error_propagation_mode_ = ErrorPropagationMode::kPropagate;
bool buffers_logically_allocated_ = false;
// Constraints as set by:
//
// v1:
// optional SetConstraintsAuxBuffers
// SetConstraints()
//
// v2 (TODO):
// SetConstraints()
//
// Either way, the constraints here are in v2 form.
std::optional<TableHolder<fuchsia_sysmem2::wire::BufferCollectionConstraints>>
buffer_collection_constraints_;
// These counts are for the current NodeProperties + any current children of the current
// NodeProperties. For LogicalBufferCollection::root_, these counts are for the whole tree.
uint32_t node_count_ = 0;
uint32_t connected_client_count_ = 0;
uint32_t buffer_collection_count_ = 0;
uint32_t buffer_collection_token_count_ = 0;
};
} // namespace sysmem_driver
#endif // SRC_DEVICES_SYSMEM_DRIVERS_SYSMEM_NODE_PROPERTIES_H_