| // 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. |
| library fuchsia.sysmem; |
| |
| using zx; |
| |
| const MAX_COUNT_DUPLICATES uint32 = 64; |
| |
| /// Actually creating this many children isn't recommended in most typical |
| /// scenarios, but isn't prevented, for testing reasons, and just in case an |
| /// unusual scenario needs it. Mitigation of potentially high time complexity |
| /// in sysmem will limit the actual number of group child combinations |
| /// considered in aggregation attempts to a separate maximum that is not |
| /// settable via these protocols. The maximum number of total nodes in a sysmem |
| /// token tree is limited to a separate maximum that is not settable via these |
| /// protocols. |
| @available(added=9) |
| const MAX_COUNT_CREATE_CHILDREN int32 = 64; |
| |
| // A protocol mixin for all nodes in the tree established by |
| // BufferCollectionToken creation and BufferCollectionTokenGroup creation, |
| // including BufferCollectionToken(s) which have since been converted to a |
| // BufferCollection channel. |
| @available(added=9) |
| protocol Node { |
| // Sync() |
| // |
| /// Ensure that previous messages, including Duplicate() messages on a |
| /// token, collection, or group, have been received server side. |
| /// |
| /// Calling BufferCollectionToken.Sync() on a token that isn't/wasn't a |
| /// valid sysmem token risks the Sync() hanging forever. See |
| /// ValidateBufferCollectionToken() for one way to mitigate the possibility |
| /// of a hostile/fake BufferCollectionToken at the cost of one round trip. |
| /// Another way is to pass the token to BindSharedCollection(), which also |
| /// validates the token as part of exchanging it for a BufferCollection |
| /// channel, and BufferCollection Sync() can then be used. |
| /// |
| /// After a Sync(), it's then safe to send the client end of token_request |
| /// to another participant knowing the server will recognize the token when |
| /// it's sent into BindSharedCollection() by the other participant. |
| /// |
| /// Other options include waiting for each token.Duplicate() to complete |
| /// individually (using separate call to token.Sync() after each), or |
| /// calling Sync() on BufferCollection after the token has been turned in |
| /// via BindSharedCollection(). |
| /// |
| /// Another way to mitigate is to avoid calling Sync() on the token, and |
| /// instead later deal with potential failure of BufferCollection.Sync() if |
| /// the original token was invalid. This option can be preferable from a |
| /// performance point of view, but requires client code to delay sending |
| /// tokens duplicated from this token until after client code has converted |
| /// the duplicating token to a BufferCollection and received successful |
| /// response from BufferCollection.Sync(). |
| /// |
| /// Prefer using BufferCollection.Sync() instead, when feasible (see above). |
| /// When BufferCollection.Sync() isn't feasible, the caller must already |
| /// know that this token is/was valid, or BufferCollectionToken.Sync() may |
| /// hang forever. See ValidateBufferCollectionToken() to check token |
| /// validity first if the token isn't already known to be (is/was) valid. |
| @available(added=9) |
| Sync() -> (); |
| |
| // Close() |
| // |
| /// On a BufferCollectionToken channel: |
| /// |
| /// Normally a participant will convert a BufferCollectionToken into a |
| /// BufferCollection view, but a particpant is also free to Close() the |
| /// token (and then close the channel immediately or shortly later in |
| /// response to server closing its end), which avoids causing logical buffer |
| /// collection failure. Normally an unexpected token channel close will |
| /// cause logical buffer collection failure (the only exceptions being |
| /// certain cases involving AttachToken() or SetDispensable()). |
| /// |
| /// On a BufferCollection channel: |
| /// |
| /// By default the server handles unexpected failure of a BufferCollection |
| /// by failing the whole logical buffer collection. Partly this is to |
| /// expedite closing VMO handles to reclaim memory when any participant |
| /// fails. If a participant would like to cleanly close a BufferCollection |
| /// view without causing logical buffer collection failure, the participant |
| /// can send Close() before closing the client end of the BufferCollection |
| /// channel. If this is the last BufferCollection view, the logical buffer |
| /// collection will still go away. The Close() can occur before or after |
| /// SetConstraints(). If before SetConstraints(), the buffer collection |
| /// won't require constraints from this node in order to allocate. If |
| /// after SetConstraints(), the constraints are retained and aggregated |
| /// along with any subsequent logical allocation(s), despite the lack of |
| /// channel connection. |
| /// |
| /// On a BufferCollectionTokenGroup channel: |
| /// |
| /// By default, unexpected failure of a BufferCollectionTokenGroup will |
| /// trigger failure of the logical BufferCollectionTokenGroup and will |
| /// propagate failure to its parent. To close a BufferCollectionTokenGroup |
| /// channel without failing the logical group or propagating failure, send |
| /// Close() before closing the channel client endpoint. |
| /// |
| /// If Close() occurs before AllChildrenPresent(), the logical buffer |
| /// collection will still fail despite the Close() (because sysmem can't be |
| /// sure whether all relevant children were created, so it's ambiguous |
| /// whether all relevant constraints will be provided to sysmem). If |
| /// Close() occurs after AllChildrenPresent(), the children and all their |
| /// constraints remain intact (just as they would if the |
| /// BufferCollectionTokenGroup channel had remained open), and the close |
| /// doesn't trigger or propagate failure. |
| @available(added=9) |
| Close(); |
| |
| /// Set a name for VMOs in this buffer collection. The name may be truncated |
| /// shorter. The name only affects VMOs allocated after it's set - this call |
| /// does not rename existing VMOs. If multiple clients set different names |
| /// then the larger priority value will win. |
| @available(added=9) |
| SetName(struct { |
| priority uint32; |
| name string:64; |
| }); |
| |
| /// Set information about the current client that can be used by sysmem to |
| /// help debug leaking memory and hangs waiting for constraints. |name| can |
| /// be an arbitrary string, but the current process name (see |
| /// fsl::GetCurrentProcessName()) is a good default. |id| can be an |
| /// arbitrary id, but the current process ID (see |
| /// fsl::GetCurrentProcessKoid()) is a good default. |
| /// |
| /// Also used when verbose logging is enabled (see SetVerboseLogging()) to |
| /// indicate which client is closing their channel first, leading to |
| /// sub-tree failure (which can be normal if the purpose of the sub-tree is |
| /// over, but if happening earlier than expected, the |
| /// client-channel-specific name can help diagnose where the failure is |
| /// first coming from, from sysmem's point of view). |
| /// |
| /// By default (unless overriden by this message or using |
| /// Allocator.SetDebugClientInfo()), a Node will copy info from its |
| /// parent Node at the time the child Node is created. While this can be |
| /// better than nothing, it's often better for each participant to use |
| /// Node.SetDebugClientInfo() or Allocator.SetDebugClientInfo() to keep the |
| /// info directly relevant to the current client. Also, SetVerboseLogging() |
| /// can be used to help disambiguate if a Node is suspected of having info |
| /// that was copied from its parent. |
| @available(added=9) |
| SetDebugClientInfo(struct { |
| name string:64; |
| id uint64; |
| }); |
| |
| /// Sysmem logs a warning if not all clients have set constraints 5 seconds |
| /// after creating a collection. Clients can call this method to change |
| /// when the log is printed. If multiple client set the deadline, it's |
| /// unspecified which deadline will take effect. |
| @available(added=9) |
| SetDebugTimeoutLogDeadline(struct { |
| deadline zx.time; |
| }); |
| |
| // SetVerboseLogging() |
| // |
| /// Verbose logging includes constraints set via SetConstraints() from each |
| /// client along with info set via SetDebugClientInfo() and the structure of |
| /// the tree of Node(s). |
| /// |
| /// Normally sysmem prints only a single line complaint when aggregation |
| /// fails, with just the specific detailed reason that aggregation failed, |
| /// with minimal context. While this is often enough to diagnose a problem |
| /// if only a small change was made and the system had been working before |
| /// the small change, it's often not particularly helpful for getting a new |
| /// buffer collection to work for the first time. Especially with more |
| /// complex trees of nodes, involving things like AttachToken(), |
| /// SetDispensable(), BufferCollectionTokenGroup nodes, and associated |
| /// sub-trees of nodes, verbose logging may help in diagnosing what the tree |
| /// looks like and why it's failing a logical allocation, or why a tree or |
| /// sub-tree is failing sooner than expected. |
| /// |
| /// The intent of the extra logging is to be acceptable from a performance |
| /// point of view, if only enabled on a low number of buffer collections. |
| /// If we're not tracking down a bug, we shouldn't send this message. |
| /// |
| /// If too many participants leave verbose logging enabled, we may end up |
| /// needing to require that system-wide sysmem verbose logging be permitted |
| /// via some other setting, to avoid sysmem spamming the log too much due to |
| /// this message. |
| /// |
| /// This may be a NOP for some nodes due to intentional policy associated |
| /// with the node, if we don't trust a node enough to let it turn on verbose |
| /// logging. |
| @available(added=9) |
| SetVerboseLogging(); |
| |
| // GetNodeRef() |
| // |
| /// This gets an event handle that can be used as a parameter to |
| /// IsAlternateFor() called on any Node. The client will not be granted the |
| /// right to signal this event, as this handle should only be used as proof |
| /// that the client obtained this handle from this Node. |
| /// |
| /// Because this is a get not a set, no Sync() is needed between the |
| /// GetNodeRef() and the call to IsAlternateFor(), despite the two calls |
| /// potentially being on different channels. |
| /// |
| /// See also IsAlternateFor(). |
| @available(added=9) |
| GetNodeRef() -> (resource struct { |
| node_ref zx.handle:EVENT; |
| }); |
| |
| // IsAlternateFor() |
| // |
| /// This checks whether the calling node is in a subtree rooted at a |
| /// different child token of a common parent BufferCollectionTokenGroup, in |
| /// relation to the passed-in node_ref. |
| /// |
| /// This call is for assisting with admission control de-duplication, and |
| /// with debugging. |
| /// |
| /// The node_ref must be obtained using GetNodeRef() of a |
| /// BufferCollectionToken, BufferCollection, or BufferCollectionTokenGroup. |
| /// |
| /// The node_ref can be a duplicated handle; it's not necessary to call |
| /// GetNodeRef() for every call to IsAlternateFor(). |
| /// |
| /// If a calling token may not actually be a valid token at all due to |
| /// a potentially hostile/untrusted provider of the token, call |
| /// ValidateBufferCollectionToken() first instead of potentially getting |
| /// stuck indefinitely if IsAlternateFor() never responds due to a calling |
| /// token not being a real token (not really talking to sysmem). Another |
| /// option is to call BindSharedCollection with this token first which also |
| /// validates the token along with converting it to a BufferCollection, then |
| /// call BufferCollection IsAlternateFor(). |
| /// |
| /// error values: |
| /// |
| /// ZX_ERR_NOT_FOUND means the node_ref wasn't found within the same logical |
| /// buffer collection as the calling Node. Before logical allocation and |
| /// within the same logical allocation sub-tree, this essentially means that |
| /// the node_ref was never part of this logical buffer collection, since |
| /// before logical allocation all node_refs that come into existence remain |
| /// in existence at least until logical allocation (including Node(s) that |
| /// have done a Close() and closed their channel), and for ZX_ERR_NOT_FOUND |
| /// to be returned, this Node's channel needs to still be connected server |
| /// side, which won't be the case if the whole logical allocation has |
| /// failed. After logical allocation or in a different logical allocation |
| /// sub-tree there are additional potential reasons for this error. For |
| /// example a different logical allocation (separated from this Node(s) |
| /// logical allocation by an AttachToken() or SetDispensable()) can fail its |
| /// sub-tree deleting those Node(s), or a BufferCollectionTokenGroup may |
| /// exist and may select a different child sub-tree than the sub-tree the |
| /// node_ref is in causing deletion of the node_ref Node. The only time |
| /// sysmem keeps a Node around after that Node has no corresponding channel |
| /// is when Close() is used and the Node's sub-tree has not yet failed. |
| /// Another reason for this error is if the node_ref is an eventpair handle |
| /// with sufficient rights, but isn't actually a real node_ref obtained from |
| /// GetNodeRef(). |
| /// |
| /// ZX_ERR_INVALID_ARGS means the caller passed a node_ref that isn't an |
| /// eventpair handle, or doesn't have the needed rights expected on a real |
| /// node_ref. |
| /// |
| /// No other failing status codes are returned by this call. However, |
| /// sysmem may add additional codes in future, so the client should have |
| /// sensible default handling for any failing status code. |
| /// |
| /// On success, is_alternate has the following meaning: |
| /// * true - The first parent node in common between the calling node and |
| /// the node_ref Node is a BufferCollectionTokenGroup. This means that |
| /// the calling Node and the node_ref Node will _not_ have both their |
| /// constraints apply - rather sysmem will choose one or the other of |
| /// the constraints - never both. This is because only one child of |
| /// a BufferCollectionTokenGroup is selected during logical allocation, |
| /// with only that one child's sub-tree contributing to constraints |
| /// aggregation. |
| /// * false - The first parent node in common between the calling Node and |
| /// the node_ref Node is not a BufferCollectionTokenGroup. Currently, |
| /// this means the first parent node in common is a |
| /// BufferCollectionToken or BufferCollection (regardless of not |
| /// Close()ed or Close()ed). This means that the calling Node and the |
| /// node_ref Node _may_ have both their constraints apply during |
| /// constraints aggregation of the logical allocation, if both Node(s) |
| /// are selected by any parent BufferCollectionTokenGroup(s) involved. |
| /// In this case, there is no BufferCollectionTokenGroup that will |
| /// directly prevent the two Node(s) from both being selected and their |
| /// constraints both aggregated, but even when false, one or both |
| /// Node(s) may still be eliminated from consideration if one or both |
| /// Node(s) has a direct or indirect parent BufferCollectionTokenGroup |
| /// which selects a child sub-tree other than the sub-tree containing |
| /// the calling Node or node_ref Node. |
| @available(added=9) |
| IsAlternateFor(resource struct { |
| node_ref zx.handle:EVENT; |
| }) -> (struct { |
| is_alternate bool; |
| }) error zx.status; |
| }; |
| |
| /// A BufferCollectionToken is not a BufferCollection, but rather a way to |
| /// identify a potential shared BufferCollection prior to the BufferCollection |
| /// being allocated. |
| /// |
| /// We use a channel for the BufferCollectionToken instead of a single eventpair |
| /// (pair) because this way we can detect error conditions like a participant |
| /// dying mid-create. |
| // LINT.IfChange |
| protocol BufferCollectionToken { |
| @available(added=9) |
| compose Node; |
| |
| // See Node for non-deprecated equivalents. Re-building existing code will |
| // use the Node equivalents instead. These ordinals will eventually not be |
| // recognized by sysmem. |
| @available(added=7, deprecated=8, removed=9) |
| Sync() -> (); |
| // Still handled, for clients that haven't been re-built yet. |
| @available(added=9) |
| @selector("Sync") |
| DeprecatedSync() -> (); |
| |
| @available(added=7, deprecated=8, removed=9) |
| Close(); |
| // Still handled, for clients that haven't been re-built yet. |
| @available(added=9) |
| @selector("Close") |
| DeprecatedClose(); |
| |
| @available(added=7, deprecated=8, removed=9) |
| SetName(struct { |
| priority uint32; |
| name string:64; |
| }); |
| // Still handled, for clients that haven't been re-built yet. |
| @available(added=9) |
| @selector("SetName") |
| DeprecatedSetName(struct { |
| priority uint32; |
| name string:64; |
| }); |
| |
| @available(added=7, deprecated=8, removed=9) |
| SetDebugClientInfo(struct { |
| name string:64; |
| id uint64; |
| }); |
| // Still handled, for clients that haven't been re-built yet. |
| @available(added=9) |
| @selector("SetDebugClientInfo") |
| DeprecatedSetDebugClientInfo(struct { |
| name string:64; |
| id uint64; |
| }); |
| |
| @available(added=7, deprecated=8, removed=9) |
| SetDebugTimeoutLogDeadline(struct { |
| deadline zx.time; |
| }); |
| // Still handled, for clients that haven't been re-built yet. |
| @available(added=9) |
| @selector("SetDebugTimeoutLogDeadline") |
| DeprecatedSetDebugTimeoutLogDeadline(struct { |
| deadline zx.time; |
| }); |
| |
| /// This method can be used to add more participants prior to creating a |
| /// shared BufferCollection. A new token will be returned for each entry in |
| /// the `rights_attenuation_masks` array. The return value is the client |
| /// ends of each new participant token. |
| /// |
| /// If the calling token may not actually be a valid token at all due to |
| /// a potentially hostile/untrusted provider of the token, consider using |
| /// ValidateBufferCollectionToken() first instead of potentially getting |
| /// stuck indefinitely if DuplicateSync() never responds due to the calling |
| /// token not being a real token. |
| /// |
| /// In contrast to Duplicate(), no Sync() (see "protocol Node") is needed |
| /// after calling this method. |
| /// |
| /// All tokens must be turned in via BindSharedCollection() or Close() for a |
| /// BufferCollection to be successfully created. |
| /// |
| /// In each entry of `rights_attenuation_masks`, rights bits that are zero |
| /// will be absent in the buffer VMO rights obtainable via the corresponding |
| /// returned token. This allows an initiator or intermediary participant to |
| /// attenuate the rights available to a participant. This does not allow a |
| /// participant to gain rights that the participant doesn't already have. |
| /// The value ZX_RIGHT_SAME_RIGHTS can be used to specify that no |
| /// attenuation should be applied. |
| DuplicateSync(struct { |
| rights_attenuation_masks vector<zx.rights>:MAX_COUNT_DUPLICATES; |
| }) -> (resource struct { |
| tokens vector<client_end:BufferCollectionToken>:MAX_COUNT_DUPLICATES; |
| }); |
| |
| /// This method can be used to add a participant prior to creating a shared |
| /// BufferCollection. It should only be used instead of DuplicateSync in |
| /// performance sensitive cases where it would be undesireable to wait for |
| /// sysmem to respond as part of each duplicate. |
| /// |
| /// After sending one or more Duplicate() messages, and before sending the |
| /// created tokens to other participants (or to other Allocator channels), |
| /// the client should send a Sync() and wait for its response. The Sync() |
| /// call can be made on the token, or on the BufferCollection obtained by |
| /// passing this token to BindSharedCollection(). Either will ensure that |
| /// the server knows about the tokens created via Duplicate() before the |
| /// other participant sends the token to the server via separate Allocator |
| /// channel. |
| /// |
| /// All tokens must be turned in via BindSharedCollection() or Close() for a |
| /// BufferCollection to be successfully created. |
| /// |
| /// When a client calls BindSharedCollection() to turn in a |
| /// BufferCollectionToken, the server will process all Duplicate() messages |
| /// before closing down the BufferCollectionToken. This allows the client |
| /// to Duplicate() and immediately turn in the BufferCollectionToken using |
| /// BindSharedCollection, then later transfer the client end of token_request |
| /// to another participant - the server will notice the existence of the |
| /// token_request before considering this BufferCollectionToken fully closed. |
| /// |
| /// `rights_attenuation_mask` rights bits that are zero in this mask will be |
| /// absent in the buffer VMO rights obtainable via the client end of |
| /// token_request. This allows an initiator or intermediary participant to |
| /// attenuate the rights available to a participant. This does not allow a |
| /// participant to gain rights that the participant doesn't already have. |
| /// The value ZX_RIGHT_SAME_RIGHTS can be used to specify that no |
| /// attenuation should be applied. |
| /// |
| /// These values for rights_attenuation_mask result in no attenuation: |
| /// * ZX_RIGHT_SAME_RIGHTS (preferred) |
| /// * 0xFFFFFFFF (this is reasonable when an attenuation mask is computed) |
| /// * 0 (deprecated - do not use 0 - an ERROR will go to the log) |
| /// |
| /// `token_request` is the server end of a BufferCollectionToken channel. |
| /// The client end of this channel acts as another participant in creating the |
| /// shared BufferCollection. |
| Duplicate(resource struct { |
| rights_attenuation_mask uint32; |
| token_request server_end:BufferCollectionToken; |
| }); |
| |
| // SetDispensable() |
| // |
| /// A dispensable token can fail after buffers are logically allocated |
| /// without causing failure of its parent (if any). |
| /// |
| /// The dispensable token participates in constraints aggregation along with |
| /// its parent before logical buffer allocation. If the dispensable token |
| /// fails before buffers are logically allocated, the failure propagates to |
| /// the dispensable token's parent. |
| /// |
| /// After buffers are logically allocated, failure of the dispensable token |
| /// (or any child of the dispensable token) does not propagate to the |
| /// dispensable token's parent. Failure does propagate from a normal |
| /// child of a dispensable token to the dispensable token. Failure |
| /// of a child is blocked from reaching its parent if the child is attached, |
| /// or if the child is dispensable and the failure occurred after logical |
| /// allocation. |
| /// |
| /// A dispensable token can be used in cases where a participant needs to |
| /// provide constraints, but after buffers are allocated, the participant |
| /// can fail without causing buffer collection failure from the parent's |
| /// point of view. |
| /// |
| /// In contrast, AttachToken() can be used to create a token which does not |
| /// participate in constraints aggregation with its parent, and whose |
| /// failure at any time does not propagate to its parent, and whose delay |
| /// providing constraints does not prevent the parent from completing its |
| /// buffer allocation. |
| /// |
| /// An initiator may in some scenarios choose to initially use a dispensable |
| /// token for a given instance of a participant, and then later if the first |
| /// instance of that participant fails, a new second instance of that |
| /// participant my be given a token created with AttachToken(). |
| /// |
| /// If a client uses this message, the client should not rely on the |
| /// client's own BufferCollectionToken or BufferCollection channel to close |
| /// from the server end due to abrupt failure of any BufferCollectionToken |
| /// or BufferCollection that the client has SetDispensable() and given out |
| /// to another process. For this reason, the client should take extra care |
| /// to notice failure of that other process via other means. |
| /// |
| /// While it is possible (and potentially useful) to SetDispensable() on a |
| /// direct child of a BufferCollectionTokenGroup, it isn't possible to later |
| /// replace a failed dispensable token that was a direct child of a group |
| /// with a new token using AttachToken() (since there's no AttachToken() on |
| /// a group). Instead, to enable AttachToken() replacement in this case, |
| /// create an additional non-dispensable token (node) that's a direct child |
| /// of the group and make the existing dispensable token a child of the |
| /// additional token (node). This way, the additional token (node) that is |
| /// a direct child of the group has BufferCollection.AttachToken() which can |
| /// be used to replace the failed dispensable token. |
| /// |
| /// SetDispensable() on an already-dispensable token is idempotent. |
| SetDispensable(); |
| |
| // CreateBufferCollectionTokenGroup() |
| // |
| /// Most sysmem clients and many participants don't need to care about this |
| /// message or about BufferCollectionTokenGroup(s) in general. |
| /// |
| /// A BufferCollectionTokenGroup is used to create a 1 of N OR among N child |
| /// tokens. The child tokens which are not selected during aggregation will |
| /// fail (close), which a potential participant should notice when their |
| /// BufferCollection channel client endpoint sees PEER_CLOSED, allowing the |
| /// participant to clean up the speculative usage that didn't end up |
| /// happening (similarly to a normal BufferCollection server end closing |
| /// on failure of a logical buffer collection). |
| /// |
| /// See comments on protocol BufferCollectionTokenGroup. |
| /// |
| /// Any rights_attenuation_mask or AttachToken()/SetDispensable() to be |
| /// applied to the whole group can be achieved with a token for this purpose |
| /// as a direct parent of the group. |
| /// |
| /// group_request - the server end of a BufferCollectionTokenGroup channel |
| /// to be served by sysmem. |
| @available(added=9) |
| CreateBufferCollectionTokenGroup(resource struct { |
| group_request server_end:BufferCollectionTokenGroup; |
| }); |
| }; |
| // LINT.ThenChange(//src/devices/sysmem/drivers/sysmem/combined_token.fidl) |
| |
| /// BufferCollection is a connection directly from a participant to sysmem re. |
| /// a logical BufferCollection; typically the logical BufferCollection is shared |
| /// with other participants. In other words, an instance of the BufferCollection |
| /// interface is a view of a "logical buffer collection". |
| /// |
| /// This connection exists to facilitate async indication of when the logical |
| /// BufferCollection has been populated with buffers. |
| /// |
| /// Also, the channel's closure by the server is an indication to the client |
| /// that the client should close all VMO handles that were obtained from the |
| /// BufferCollection ASAP. |
| /// |
| /// Also, this interface may in future allow specifying constraints in other |
| /// ways, and may allow for back-and-forth negotiation of constraints to some |
| /// degree. |
| /// |
| /// This interface may in future allow for more than 64 VMO handles per |
| /// BufferCollection, but currently the limit is 64. |
| /// |
| /// This interface may in future allow for allocating/deallocating single |
| /// buffers. |
| /// |
| /// Some initiators may wait a short duration until all old logical |
| /// BufferCollection VMO handles have closed (or until the short duration times |
| /// out) before allocating a new BufferCollection, to help control physical |
| /// memory fragmentation and avoid overlap of buffer allocation lifetimes for |
| /// the old and new collections. Collections can be large enough that it's worth |
| /// avoiding allocation overlap (in time). |
| protocol BufferCollection { |
| @available(added=9) |
| compose Node; |
| |
| // See Node for non-deprecated equivalents. Re-building existing code will |
| // use the Node equivalents instead. These ordinals will eventually not be |
| // recognized by sysmem. |
| @available(added=7, deprecated=8, removed=9) |
| Sync() -> (); |
| @available(added=9) |
| @selector("Sync") |
| DeprecatedSync() -> (); |
| |
| @available(added=7, deprecated=8, removed=9) |
| Close(); |
| @available(added=9) |
| @selector("Close") |
| DeprecatedClose(); |
| |
| @available(added=7, deprecated=8, removed=9) |
| SetName(struct { |
| priority uint32; |
| name string:64; |
| }); |
| @available(added=9) |
| @selector("SetName") |
| DeprecatedSetName(struct { |
| priority uint32; |
| name string:64; |
| }); |
| |
| @available(added=7, deprecated=8, removed=9) |
| SetDebugClientInfo(struct { |
| name string:64; |
| id uint64; |
| }); |
| @available(added=9) |
| @selector("SetDebugClientInfo") |
| DeprecatedSetDebugClientInfo(struct { |
| name string:64; |
| id uint64; |
| }); |
| |
| /// Provide BufferCollectionConstraints to the logical BufferCollection. |
| /// |
| /// A participant may only call SetConstraints() once. |
| /// |
| /// Sometimes the initiator is a participant only in the sense of wanting to |
| /// keep an eye on success/failure to populate with buffers, and zx.status |
| /// on failure. In that case, `has_constraints` can be false, and |
| /// `constraints` will be ignored. |
| /// |
| /// VMO handles will not be provided to the client that sends null |
| /// constraints - that can be intentional for an initiator that doesn't need |
| /// VMO handles. Not having VMO handles doesn't prevent the initator from |
| /// adjusting which portion of a buffer is considered valid and similar, but |
| /// the initiator can't hold a VMO handle open to prevent the logical |
| /// BufferCollection from cleaning up if the logical BufferCollection needs |
| /// to go away regardless of the initiator's degree of involvement for |
| /// whatever reason. |
| /// |
| /// For population of buffers to be attempted, all holders of a |
| /// BufferCollection client channel need to call SetConstraints() before |
| /// sysmem will attempt to allocate buffers. |
| /// |
| /// `has_constraints` if false, the constraints are effectively null, and |
| /// `constraints` are ignored. The sender of null constraints won't get any |
| /// VMO handles in BufferCollectionInfo, but can still find out how many |
| /// buffers were allocated and can still refer to buffers by their |
| /// buffer_index. |
| /// |
| /// `constraints` are constraints on the buffer collection. |
| SetConstraints(struct { |
| has_constraints bool; |
| constraints BufferCollectionConstraints; |
| }); |
| |
| /// This request completes when buffers have been allocated, responds with |
| /// some failure detail if allocation has been attempted but failed. |
| /// |
| /// The following must occur before buffers will be allocated: |
| /// * All BufferCollectionToken(s) of the logical BufferCollectionToken |
| /// must be turned in via BindSharedCollection(). |
| /// * All BufferCollection(s) of the logical BufferCollection must have |
| /// had SetConstraints() sent to them. |
| /// |
| /// Returns `ZX_OK` if successful. |
| /// Returns `ZX_ERR_NO_MEMORY` if the request is valid but cannot be |
| /// fulfilled due to resource exhaustion. |
| /// Returns `ZX_ERR_ACCESS_DENIED` if the caller is not permitted to |
| /// obtain the buffers it requested. |
| /// Returns `ZX_ERR_INVALID_ARGS` if the request is malformed. |
| /// Returns `ZX_ERR_NOT_SUPPORTED` if request is valid but cannot be |
| /// satisfied, perhaps due to hardware limitations. |
| /// |
| /// `buffer_collection_info` has the VMO handles and other related info. |
| WaitForBuffersAllocated() -> (resource struct { |
| status zx.status; |
| buffer_collection_info BufferCollectionInfo_2; |
| }); |
| |
| /// This returns the same result code as WaitForBuffersAllocated if the |
| /// buffer collection has been allocated or failed, or `ZX_ERR_UNAVAILABLE` |
| /// if WaitForBuffersAllocated would block. |
| CheckBuffersAllocated() -> (struct { |
| status zx.status; |
| }); |
| |
| @available(added=7, deprecated=8, removed=9) |
| AllocateSingleBuffer(struct { |
| buffer_index uint64; |
| }); |
| @available(added=7, deprecated=8, removed=9) |
| CloseSingleBuffer(struct { |
| buffer_index uint64; |
| }); |
| @available(added=7, deprecated=8, removed=9) |
| WaitForSingleBufferAllocated(struct { |
| buffer_index uint64; |
| }) -> (resource struct { |
| status zx.status; |
| buffer_info SingleBufferInfo; |
| }); |
| @available(added=7, deprecated=8, removed=9) |
| CheckSingleBufferAllocated(struct { |
| buffer_index uint64; |
| }); |
| |
| /// Deprecated |
| /// |
| /// Optionally sent before SetConstraints() to set constraints related to |
| /// clear (not encrypted, not in protected/secure memory) aux buffers. This |
| /// is only valid when sent before SetConstraints(). Invalid settings may |
| /// not result in channel closure until SetConstraints() is received by |
| /// sysmem. |
| @available(added=7, deprecated=9) |
| SetConstraintsAuxBuffers(struct { |
| constraints BufferCollectionConstraintsAuxBuffers; |
| }); |
| |
| /// Deprecated |
| /// |
| /// Allows getting any aux buffers allocated after using |
| /// SetConstraintsAuxBuffers(). |
| /// |
| /// Sending this message is not permitted until WaitForBuffersAllocated() |
| /// completes. |
| /// |
| /// On failure, status will be a failing status and |
| /// buffer_collection_info_aux_buffers won't be filled out, and won't have |
| /// any handles. |
| /// |
| /// On success, status will be ZX_OK, and buffer_count will be the same as |
| /// the main buffer_count from WaitForBuffersAllocated(). |
| /// |
| /// If a participant specified "allow_clear_aux_buffers_for_secure" true but |
| /// "need_clear_aux_buffers_for_secure" false (or not set), the participant |
| /// can determine if aux buffers were allocated by looking at buffer[0].vmo. |
| /// If buffer 0 has no vmo, aux VMOs were not allocated. The resulting |
| /// status will still be ZX_OK in this case, and buffer_count will still be |
| /// filled out to match the main buffer_count. |
| /// |
| /// It's legal for a participant that set |
| /// "allow_clear_aux_buffers_for_secure" false to call GetAuxBuffers(), in |
| /// which case buffer[0].vmo will not be set. |
| @available(added=7, deprecated=9) |
| GetAuxBuffers() -> (resource struct { |
| status zx.status; |
| buffer_collection_info_aux_buffers BufferCollectionInfo_2; |
| }); |
| |
| /// Create a new token, for trying to add a new participant to an existing |
| /// collection, if the existing collection's buffer counts, constraints, |
| /// and participants allow. |
| /// |
| /// This can be useful in replacing a failed participant, and/or in |
| /// adding/re-adding a participant after buffers have already been |
| /// allocated. |
| /// |
| /// Failure of an attached token / collection does not propagate to the |
| /// parent of the attached token. Failure does propagate from a normal |
| /// child of a dispensable token to the dispensable token. Failure |
| /// of a child is blocked from reaching its parent if the child is attached, |
| /// or if the child is dispensable and the failure occurred after logical |
| /// allocation. |
| /// |
| /// An initiator may in some scenarios choose to initially use a dispensable |
| /// token for a given instance of a participant, and then later if the first |
| /// instance of that participant fails, a new second instance of that |
| /// participant my be given a token created with AttachToken(). |
| /// |
| /// From the point of view of the client end of the BufferCollectionToken |
| /// channel, the token acts like any other token. The client can |
| /// Duplicate() the token as needed, and can send the token to a different |
| /// process. The token should be converted to a BufferCollection channel |
| /// as normal by calling BindSharedCollection(). SetConstraints() should |
| /// be called on that BufferCollection channel. |
| /// |
| /// A success result from WaitForBuffersAllocated() means the new |
| /// participant's constraints were satisfiable using the already-existing |
| /// buffer collection, the already-established BufferCollectionInfo |
| /// including image format constraints, and the already-existing other |
| /// participants and their buffer counts. A failure result means the new |
| /// participant's constraints cannot be satisfied using the existing |
| /// buffer collection and its already-logically-allocated participants. |
| /// Creating a new collection instead may allow all participant's |
| /// constraints to be satisfied, assuming SetDispensable() is used in place |
| /// of AttachToken(), or a normal token is used. |
| /// |
| /// A token created with AttachToken() performs constraints aggregation with |
| /// all constraints currently in effect on the buffer collection, plus the |
| /// attached token under consideration plus child tokens under the attached |
| /// token which are not themselves an attached token or under such a token. |
| /// |
| /// Allocation of buffer_count to min_buffer_count_for_camping etc is |
| /// first-come first-served, but a child can't logically allocate before |
| /// all its parents have sent SetConstraints(). |
| /// |
| /// See also SetDispensable(), which in contrast to AttachToken(), has the |
| /// created token + children participate in constraints aggregation along |
| /// with its parent. |
| /// |
| /// The newly created token needs to be Sync()ed to sysmem before the new |
| /// token can be passed to BindSharedCollection(). The Sync() of the new |
| /// token can be accomplished with BufferCollection.Sync() on this |
| /// BufferCollection. Alternately BufferCollectionToken.Sync() on the new |
| /// token also works. A BufferCollectionToken.Sync() can be started after |
| /// any BufferCollectionToken.Duplicate() messages have been sent via the |
| /// newly created token, to also sync those additional tokens to sysmem |
| /// using a single round-trip. |
| /// |
| /// These values for rights_attenuation_mask result in no attenuation (note |
| /// that 0 is not on this list; 0 will output an ERROR to the system log |
| /// to help diagnose the bug in client code): |
| /// * ZX_RIGHT_SAME_RIGHTS (preferred) |
| /// * 0xFFFFFFFF (this is reasonable when an attenuation mask is computed) |
| AttachToken(resource struct { |
| rights_attenuation_mask uint32; |
| token_request server_end:BufferCollectionToken; |
| }); |
| |
| /// AttachLifetimeTracking: |
| /// |
| /// AttachLifetimeTracking() is intended to allow a client to wait until an |
| /// old logical buffer collection is fully or mostly deallocated before |
| /// attempting allocation of a new logical buffer collection. |
| /// |
| /// Attach an eventpair endpoint to the logical buffer collection, so that |
| /// the server_end will be closed when the number of buffers allocated |
| /// drops to 'buffers_remaining'. The server_end won't close until after |
| /// logical allocation has completed. |
| /// |
| /// If logical allocation fails, such as for an attached sub-tree (using |
| /// AttachToken()), the server_end will close during that failure regardless |
| /// of the number of buffers potenitally allocated in the overall logical |
| /// buffer collection. |
| /// |
| /// Multiple eventpair endpoints can be attached, with an enforced limit of |
| /// SYSMEM_LIFETIME_TRACKING_EVENTPAIR_PER_BUFFER_COLLECTION_CHANNEL_MAX. |
| /// |
| /// The lifetime signalled by this event includes asynchronous cleanup of |
| /// allocated buffers, and this asynchronous cleanup cannot occur until all |
| /// holders of VMO handles to the buffers have closed those VMO handles. |
| /// Therefore clients should take care not to become blocked forever waiting |
| /// for ZX_EVENTPAIR_PEER_CLOSED to be signalled, especially if any of the |
| /// participants using the logical buffer collection are less trusted or |
| /// less reliable. |
| /// |
| /// The buffers_remaining parameter allows waiting for all but |
| /// buffers_remaining buffers to be fully deallocated. This can be useful |
| /// in situations where a known number of buffers are intentionally not |
| /// closed so that the data can continue to be used, such as for keeping the |
| /// last available video picture displayed in the UI even if the video |
| /// stream was using protected output buffers. It's outside the scope of |
| /// the BufferCollection interface (at least for now) to determine how many |
| /// buffers may be held without closing, but it'll typically be in the range |
| /// 0-2. |
| /// |
| /// This mechanism is meant to be compatible with other protocols providing |
| /// a similar AttachLifetimeTracking() mechanism, in that duplicates of the |
| /// same event can be sent to more than one AttachLifetimeTracking(), and |
| /// the ZX_EVENTPAIR_PEER_CLOSED will be signalled when all the lifetime |
| /// over conditions are met (all holders of duplicates have closed their |
| /// handle(s)). |
| /// |
| /// A maximum of |
| /// SYSMEM_LIFETIME_TRACKING_EVENTPAIR_PER_BUFFER_COLLECTION_CHANNEL_MAX |
| /// AttachLifetimeTracking() messages are allowed per BufferCollection |
| /// channel. |
| /// |
| /// There is no way to cancel an attach. Closing the client end of the |
| /// eventpair doesn't subtract from the number of pending attach(es). |
| /// |
| /// Closing the client's end doesn't result in any action by the server. |
| /// If the server listens to events from the client end at all, it is for |
| /// debug logging only. |
| /// |
| /// The server intentionally doesn't "trust" any bits signalled by the |
| /// client. This mechanism intentionally uses only ZX_EVENTPAIR_PEER_CLOSED |
| /// which can't be triggered early, and is only triggered when all handles |
| /// to server_end are closed. No meaning is associated with any of the |
| /// other signal bits, and clients should functionally ignore any other |
| /// signal bits on either end of the eventpair or its peer. |
| /// |
| /// The server_end may lack ZX_RIGHT_SIGNAL or ZX_RIGHT_SIGNAL_PEER, but |
| /// must have ZX_RIGHT_DUPLICATE (and must have ZX_RIGHT_TRANSFER to |
| /// transfer without causing CodecFactory channel failure). |
| AttachLifetimeTracking(resource struct { |
| server_end zx.handle:EVENTPAIR; |
| buffers_remaining uint32; |
| }); |
| }; |
| |
| // BufferCollectionTokenGroup: |
| // |
| /// The sysmem implementation is guaranteed to be consistent with a logical / |
| /// conceptual model as follows: |
| /// |
| /// As usual, a logical allocation considers either the root and all nodes with |
| /// connectivity to the root that don't transit an AttachToken(), or a sub-tree |
| /// rooted at an AttachToken() token and all nodes with connectivity to that |
| /// subtree that don't transit another AttachToken(). This is called the |
| /// logical allocation pruned sub-tree, or pruned sub-tree for short. |
| /// |
| /// During constraints aggregation, each BufferCollectionTokenGroup will select |
| /// a single child token among its children. The rest of the children will |
| /// appear to fail the logical allocation, while the selected child may succeed. |
| /// |
| /// When more than one BufferCollectionTokenGroup exists in the overall logical |
| /// allocation pruned sub-tree, the relative priority between two groups is |
| /// equivalent to their ordering in a DFS pre-order iteration of the tree, with |
| /// parents higher priority than children, and left children higher priority |
| /// than right children. |
| /// |
| /// When a particular child of a group is selected (whether provisionally during |
| /// a constraints aggregation attempt, or as a final selection), the |
| /// non-selection of other children of the group can potentially "hide" other |
| /// groups under those non-selected children. |
| /// |
| /// Within a logical allocation, aggregation is attempted first by provisionally |
| /// selecting the child 0 of the highest-priority group, and child 0 of the next |
| /// highest-priority group that isn't hidden by the provisional selections so |
| /// far, etc. |
| /// |
| /// If that aggregation attempt fails, aggregation will be attempted with the |
| /// ordinal 0 child of all the same groups except the lowest priority non-hidden |
| /// group which will provisionally select its ordinal 1 child (and then child 2 |
| /// and so on). If a new lowest-priority group is un-hidden as provisional |
| /// selections are updated, that newly un-hidden lowest-priority group has all |
| /// its children considered in order, before changing the provisional selection |
| /// in the former lowest-priority group. In terms of result, this is equivalent |
| /// to systematic enumeration of all possible combinations of choices in a |
| /// counting-like order updating the lowest-priority group the most often and |
| /// the highest-priority group the least often. Rather than actually attempting |
| /// aggregation with all the combinations, we can skip over combinations which |
| /// are redundant/equivalent due to hiding without any change to the result. |
| /// |
| /// Attempted aggregations of enumerated non-equivalent combinations of choices |
| /// continue in this manner until either (a) all aggregation attempts fail in |
| /// which case the overall logical allocation fails, or (b) until an attempted |
| /// aggregation succeeds, in which case buffer allocation (if needed) is |
| /// attempted once. If buffer allocation based on the first successful |
| /// aggregation fails, the overall logical allocation fails (there is no buffer |
| /// allocation retry / re-attempt). If buffer allocation succeeds (or is not |
| /// needed), the logical allocation succeeds. |
| /// |
| /// If this prioritization scheme cannot reasonably work for your usage of |
| /// sysmem, please contact sysmem folks to discuss potentially adding a way to |
| /// achieve what you need. |
| /// |
| /// Please avoid creating a large number of BufferCollectionTokenGroup(s) per |
| /// logical allocation, especially with large number of children overall, and |
| /// especially in cases where aggregation may reasonably be expected to often |
| /// fail using ordinal 0 children and possibly with later children as well. We |
| /// anticipate mitigating potentially high time complexity of evaluating too |
| /// many child combinations/selections across too many groups by simply failing |
| /// logical allocation beyond a certain (fairly high, but not huge) max number |
| /// of considered group child combinations/selections. More advanced (and more |
| /// complicated) mitigation is not anticipated to be practically necessary or |
| /// worth the added complexity. Please contact sysmem folks if the max limit |
| /// is getting hit or if you anticipate it getting hit, to discuss potential |
| /// options. |
| /// |
| /// Prefer to use multiple ImageFormatConstraints in a single |
| /// BufferCollectionConstraints when feasible (when a participant just needs to |
| /// express the ability to work with more than a single PixelFormat, with |
| /// sysmem choosing which PixelFormat to use among those supported by all |
| /// participants). |
| /// |
| /// Similar to BufferCollectionToken and BufferCollection, closure of the |
| /// BufferCollectionTokenGroup channel without sending Close() first will cause |
| /// logical buffer collection failure (or sub-tree failure if using |
| /// SetDispensable() or AttachToken() and the BufferCollectionTokenGroup is part |
| /// of a sub-tree under such a node that doesn't propagate failure to its |
| /// parent). |
| @available(added=9) |
| protocol BufferCollectionTokenGroup { |
| compose Node; |
| |
| /// Create a child token. Before passing the client end of this token to |
| /// BindSharedCollection(), completion of Sync() after CreateChild() is |
| /// required. Or the client can use CreateChildrenSync() which essentially |
| /// includes the Sync(). |
| /// |
| /// token_request - the server end of the new token channel. |
| /// |
| /// rights_attenuation_mask - If ZX_RIGHT_SAME_RIGHTS, the created token |
| /// allows the holder to get the same rights to buffers as the parent token |
| /// (of the group) had. |
| CreateChild(resource table { |
| /// Must be set. |
| 1: token_request server_end:BufferCollectionToken; |
| |
| /// If not set, the default is ZX_RIGHT_SAME_RIGHTS. |
| 2: rights_attenuation_mask uint32; |
| }); |
| |
| /// Create 1 or more child tokens at once, synchronously. In contrast to |
| /// CreateChild(), no Sync() completion is required before passing the |
| /// client end of a returned token to BindSharedCollection(). |
| /// |
| /// The size of the rights_attentuation_mask determines the number of |
| /// created child tokens. |
| /// |
| /// The lower-index child tokens are higher priority (attempted sooner) than |
| /// higher-index child tokens. |
| /// |
| /// As per all child tokens, successful aggregation will choose exactly one |
| /// child among all created children (across all children created across |
| /// potentially multiple calls to CreateChild() and CreateChildrenSync()). |
| /// |
| /// The maximum permissible total number of children per group, and total |
| /// number of nodes in an overall tree (from the root) are capped to limits |
| /// which are not configurable via these protocols. |
| CreateChildrenSync(struct { |
| rights_attenuation_masks vector<zx.rights>:MAX_COUNT_CREATE_CHILDREN; |
| }) -> (resource struct { |
| tokens vector<client_end:BufferCollectionToken>:MAX_COUNT_CREATE_CHILDREN; |
| }); |
| |
| /// AllChildrenPresent() |
| /// |
| /// After creating all children, the client must call AllChildrenPresent() |
| /// to inform sysmem that no more children will be created, so that sysmem |
| /// can know when it's ok to start aggregating constraints. |
| /// |
| /// If Close() is to be sent, it should be sent _after_ |
| /// AllChildrenPresent(), else failure of the group and propagation of the |
| /// failure to the group's parent will still be triggered. |
| AllChildrenPresent(); |
| }; |