| // 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_ |