| // 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; |
| |
| // 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. |
| [Discoverable, Layout = "Simple"] |
| protocol BufferCollectionToken { |
| // The initiator or a participant can send Duplicate() as part of creating |
| // another participant-side handle to the same logical |
| // BufferCollectionToken. |
| // |
| // This method is used to hand the logical token to all participants so all |
| // participants can provide constraints to sysmem for the overall |
| // BufferCollection to achieve the goal of allocating buffers compatible |
| // with all participants. |
| // |
| // The Duplicate() message is intentionally available only on |
| // BufferCollectionToken not BufferCollection. |
| // |
| // The token is separate from BufferCollection so that participants contact |
| // sysmem directly, so that participants are only trusting their environment |
| // for who sysmem is (fake token mitigation), not an initiator. Only after |
| // successful BindSharedCollection does a participant know that the token |
| // was a real sysmem token. In contrast, if we had Duplicate() directly on |
| // BufferCollection, an initiator could attempt to serve the |
| // BufferCollection channel itself, which would allow for some problematic |
| // possibilities. |
| // |
| // All the BufferCollectionToken channels of a logical token must be turned |
| // in via BindSharedCollection() for a BufferCollection to be successfully |
| // created. Else the BufferCollection channel will close. |
| // |
| // 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 may not be the |
| // only mechanism that attenuates rights on the VMO handles obtainable via |
| // the client end of token_request. This does not allow a participant |
| // to gain rights that the participant doesn't already have. |
| // |
| // |token_request| is the server end of a BufferCollectionToken channel. |
| // The client end of this channel acts as another handle to the same logical |
| // BufferCollectionToken. Typically the sender of Duplicate() will transfer |
| // the client end corresponding to collection_request to a/another |
| // participant running in a separate process, but it's also fine for the |
| // additional logical participant to be in the same process. |
| // |
| // After sending one or more Duplicate() messages, and before sending the |
| // created tokens to other participants (or to other Allocator2 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 Allocator2 |
| // channel. If a client is using FIDL C generated code and doesn't want to |
| // block waiting for a response message, the other option is to notice |
| // arrival of the BufferCollectionEvents::OnBufferCollectionCreated() event |
| // after turning in this token for a BufferCollection. |
| // |
| // TODO(dustingreen): Consider other mechanisms to ensure the token created |
| // here is recognized by the server. |
| Duplicate(uint32 rights_attenuation_mask, |
| request<BufferCollectionToken> token_request); |
| |
| // Ensure that previous Duplicate() messages have been received server side, |
| // so that it's 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 Duplicate() to complete |
| // individually, or calling Sync() on BufferCollection after this token has |
| // been turned in via BindSharedCollection(), or noticing arrival of |
| // BufferCollectionEvents::OnDuplicatedTokensKnownByServer(). |
| Sync() -> (); |
| |
| // Normally a participant will convert the token 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 LogicalBufferCollection failure. |
| // Normally an unexpected token channel close will cause |
| // LogicalBufferCollection failure. |
| Close(); |
| }; |
| |
| // 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 "LogicalBufferCollection". |
| // |
| // 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). |
| [Discoverable, Layout = "Simple"] |
| protocol BufferCollection { |
| // At least for now, the only way to get events from a BufferCollection is |
| // to set a reverse BufferCollectionEvents channel. This can be sent up to |
| // once at any point during BufferCollection channel lifetime. All events |
| // are one-shot events, and will be sent immediately via |events| if the |
| // one-shot event's condition has already become true (once true will stay |
| // true; only goes from false to true once). |
| // |
| // |events| is the client end of a BufferCollectionEvents which will be sent |
| // one-way messages indicating events relevant to this BufferCollection |
| // channel (some may be specific to this BufferCollection channel and some |
| // may be relevant to the overall logical BufferCollection). |
| SetEventSink(BufferCollectionEvents events); |
| |
| // See comments on BufferCollectionToken::Sync(). |
| Sync() -> (); |
| |
| // Provide BufferCollectionConstraints to the logical BufferCollection. |
| // |
| // Participants with read but not write can only call SetConstraints() once. |
| // |
| // Participants with write can call SetConstraints() more than once. The |
| // initial buffer allocation will use the constraints in the first call to |
| // SetConstraints(). Among other things, this allows a decoder to attempt |
| // to allocate a new buffer that's larger to hold an output frame that's |
| // larger. |
| // |
| // 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(bool has_constraints, |
| BufferCollectionConstraints constraints); |
| |
| // 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. |
| // |
| // A caller using C generated FIDL code who wishes not to block a thread in |
| // a zx_channel_call() for a potentially fairly long duration on this |
| // message/response can use SetEventSink() and |
| // BufferCollectionEvents.OnBuffersPopulated() instead. |
| // |
| // This method is still legal to call despite use of OnBuffersPopulated(), |
| // but in that case the additional BufferCollectionInfo returned here will |
| // include handles that are redundant with other handles in the |
| // BufferCollectionInfo delivered via OnBuffersPopulated() (separate handle |
| // but same underlying VMO objects), so most clients that bother calling |
| // SetEventSink() will prefer to receive BufferCollectionInfo via |
| // OnBuffersPopulated(). This method is mostly here for clients that don't |
| // call SetEventSink(). |
| // |
| // 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() |
| -> (zx.status status, BufferCollectionInfo_2 buffer_collection_info); |
| |
| // 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() -> (zx.status status); |
| |
| // The CloseBuffer() doesn't immediately force all VMO handles to that |
| // buffer to close, but it does close any handle held by sysmem, and does |
| // notify all participants of the desire to close the buffer at which point |
| // each participant that's listening may close their handle to the buffer. |
| // |
| // Only a particpant with write can do this. Coordination among multiple |
| // participants with write is outside of the scope of this interface. |
| // |
| // |buffer_index| indicates which buffer to close. If the buffer is already |
| // closed this has no effect (idempotent). |
| CloseSingleBuffer(uint64 buffer_index); |
| |
| // This allocates a new buffer that is consistent with the most recent call |
| // to SetConstraints(), if possible. If not possible, this indicates the |
| // failure via OnNewBufferAllocated(). |
| // |
| // Only a participant with write can do this. Coordination among multiple |
| // participants with write is outside the scope of this interface. |
| // |
| // The participant is (intentionally) never informed of other participant's |
| // constraints. |
| AllocateSingleBuffer(uint64 buffer_index); |
| |
| // Completes when AllocateBuffer is done. Callers who wish to avoid |
| // blocking a thread while waiting can use OnAllocateSingleBufferDone() |
| // instead. |
| WaitForSingleBufferAllocated(uint64 buffer_index) |
| -> (zx.status status, SingleBufferInfo buffer_info); |
| |
| // A participant can use this message to have sysmem verify that this |
| // buffer_index exists. This message is intentionally ignored by the |
| // server if the buffer_index _does_ exist. In that case, the client will |
| // see OnAllocateSingleBufferDone() soon with status == ZX_OK (if the |
| // client hasn't already seen that message). If on the other hand the |
| // buffer_index does not exist, this message causes the server to send |
| // OnAllocateSingleBufferDone() with status == ZX_ERR_NOT_FOUND. A |
| // particpant will typically use this when the participant receives a new |
| // buffer_index that the participant doesn't yet know about, to ensure that |
| // the participant won't be waiting forever for the |
| // OnAllocateSingleBufferDone() message regarding this buffer_index. |
| CheckSingleBufferAllocated(uint64 buffer_index); |
| |
| // The server handles unexpected failure of a BufferCollection by failing |
| // the whole LogicalBufferCollection. Partly this is to expedite closing |
| // VMO handles. If a participant would like to cleanly close a |
| // BufferCollection view without causing LogicalBufferCollection failure, |
| // the participant can send Close() before closing the client end of the |
| // BufferCollection channel. If this is the last BufferCollection view, the |
| // LogicalBufferCollection will still go away. |
| Close(); |
| }; |
| |
| // This interface intentionally doesn't include any event for |
| // OnOldBufferClosed(), because such an event could arrive at a participant too |
| // soon to be useful. Instead, such an indication should be made in-band within |
| // FIDL interfaces that deliver packets to downstream participants. |
| [Discoverable, Layout = "Simple"] |
| protocol BufferCollectionEvents { |
| // See comments on BufferCollectionToken::Sync(). |
| // |
| // This message only indicates that the server has reached the point where |
| // it knows about previously created tokens Duplicate()ed from the token |
| // used to create this BufferCollection. |
| OnDuplicatedTokensKnownByServer(); |
| |
| // This event inidicates that buffer allocation is over, whether succesful |
| // or failed. |
| // |
| // This event will eventually be sent by the server (unless the |
| // BufferCollection channel closes first). |
| // |
| // |status|: |
| // |ZX_OK| if successful. |
| // |ZX_ERR_NO_MEMORY| if the request is valid but cannot be fulfilled due to |
| // resource exhaustion. |
| // |ZX_ERR_ACCESS_DENIED| if the caller is not permitted to obtain the |
| // buffers it requested. |
| // |ZX_ERR_INVALID_ARGS| if the request is malformed. |
| // |ZX_ERR_NOT_SUPPORTED| if request is valid but cannot be satisfied, |
| // perhaps due to hardware limitations. |
| // |
| // |buffer_collection_info| The buffer information, including VMO handles. |
| // If |status| is not |ZX_OK|, |buffer_collection_info| is default |
| // initialized and contains no meaningful information. |
| OnBuffersAllocated(zx.status status, |
| BufferCollectionInfo_2 buffer_collection_info); |
| |
| // A participant can learn when a new buffer is allocated via this event. |
| // The only participant that will see a failing status is the participant |
| // that attempted the single buffer allocation. Other participants will |
| // only see successful single buffer allocations. |
| // |
| // |status|: |
| // |
| // |ZX_OK| if successful. This can be seen by any participant (whether |
| // sender of AllocateSingleBuffer() or not.) |
| // |
| // |ZX_ERR_NOT_FOUND| if the buffer_index sent via |
| // CheckSingleBufferAllocated() isn't known to the server. This can be seen |
| // by any participant (whether sender of AllocateSingleBuffer() or not.) |
| // |
| // These error codes are only ever seen by the sender of |
| // AllocateSingleBuffer(): |
| // |
| // |ZX_ERR_NO_MEMORY| if the request is valid but cannot be fulfilled due to |
| // resource exhaustion. |
| // |ZX_ERR_ACCESS_DENIED| if the caller is not permitted to obtain the |
| // buffers it requested. |
| // |ZX_ERR_INVALID_ARGS| if the request is malformed. |
| // |ZX_ERR_NOT_SUPPORTED| if request is valid but cannot be satisfied, |
| // perhaps due to hardware limitations. |
| OnAllocateSingleBufferDone(zx.status status, |
| SingleBufferInfo buffer_info); |
| }; |