| // 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. |
| |
| #ifndef IO_SCHEDULER_STREAM_OP_H_ |
| #define IO_SCHEDULER_STREAM_OP_H_ |
| |
| #include <stdint.h> |
| #include <zircon/types.h> |
| |
| #include <fbl/intrusive_double_list.h> |
| |
| namespace ioscheduler { |
| |
| // Operation type. |
| // These are used to determine respective ordering restrictions of the ops in a stream. |
| enum class OpType : uint32_t { |
| // Operations that can optionally be reordered. |
| |
| kOpTypeUnknown = 0, // Always reordered. |
| kOpTypeRead = 1, // Read ordering. |
| kOpTypeWrite = 2, // Write order. |
| kOpTypeDiscard = 3, // Write order. |
| kOpTypeRename = 4, // Read and Write order. |
| kOpTypeSync = 5, // Write order. |
| kOpTypeCommand = 6, // Read and Write order. |
| |
| // Operations that cannot be reordered. |
| |
| kOpTypeOrderedUnknown = 32, // Always ordered. |
| |
| // Barrier operations. |
| |
| // Prevent reads from being reordered ahead of this barrier op. No read |
| // after this barrier can be issued until this operation has completed. |
| kOpTypeReadBarrier = 64, |
| |
| // Prevent writes from being reordered after this barrier op. This |
| // operation completes after all previous writes in the stream have been |
| // issued. |
| kOpTypeWriteBarrier = 65, |
| |
| // Prevent writes from being reordered after this barrier op. This |
| // instruction completes after all previous writes in the stream have been |
| // completed. |
| kOpTypeWriteCompleteBarrier = 66, |
| |
| // Combined effects of kOpTypeReadBarrier and kOpTypeWriteBarrier. |
| kOpTypeFullBarrier = 67, |
| |
| // Combined effects of kOpTypeReadBarrier and kOpTypeWriteCompleteBarrier. |
| kOpTypeFullCompleteBarrier = 68, |
| }; |
| |
| constexpr uint32_t kOpFlagComplete = (1u << 0); |
| constexpr uint32_t kOpFlagDeferred = (1u << 1); |
| constexpr uint32_t kOpFlagGroupLeader = (1u << 8); |
| |
| constexpr uint32_t kOpGroupNone = 0; |
| |
| class Stream; |
| class StreamOp; |
| |
| // UniqueOp is a wrapper around StreamOp designed to clarify the ownership of an op pointer. |
| // It supports move-only semantics, and must be either move()'d or release()'d before destruction. |
| // Since StreamOp is allocated by the client, it cannot be deleted by this wrapper. Notably, |
| // UniqueOp's destructor DOES NOT delete and will assert if its container is non-null. |
| class UniqueOp { |
| public: |
| // Constructors |
| constexpr UniqueOp() : op_(nullptr) {} |
| constexpr UniqueOp(decltype(nullptr)) : UniqueOp() {} |
| explicit UniqueOp(StreamOp* op) : op_(op) {} |
| |
| // Copy construction. |
| UniqueOp(const UniqueOp& r) = delete; |
| // Assignment |
| UniqueOp& operator=(const UniqueOp& r) = delete; |
| |
| // Move construction. |
| UniqueOp(UniqueOp&& r) : op_(r.op_) { r.op_ = nullptr; } |
| |
| ~UniqueOp() { ZX_DEBUG_ASSERT(op_ == nullptr); } |
| |
| // Move assignment. |
| UniqueOp& operator=(UniqueOp&& r) { |
| ZX_DEBUG_ASSERT(op_ == nullptr); |
| op_ = r.op_; |
| r.op_ = nullptr; |
| return *this; |
| } |
| |
| void set(StreamOp* op) { |
| ZX_DEBUG_ASSERT(op_ == nullptr); |
| op_ = op; |
| } |
| |
| StreamOp* release() { |
| StreamOp* old = op_; |
| op_ = nullptr; |
| return old; |
| } |
| |
| StreamOp* get() const { return op_; } |
| StreamOp& operator*() const { return *op_; } |
| StreamOp* operator->() const { return op_; } |
| explicit operator bool() const { return !!op_; } |
| bool operator==(decltype(nullptr)) const { return (op_ == nullptr); } |
| bool operator!=(decltype(nullptr)) const { return (op_ != nullptr); } |
| bool operator==(const UniqueOp& other) const { return (op_ == other.op_); } |
| bool operator!=(const UniqueOp& other) const { return (op_ != other.op_); } |
| |
| private: |
| StreamOp* op_ = nullptr; |
| }; |
| |
| // Tag Types used to manage the different intrusive containers that a StreamOp |
| // can exist in. |
| namespace internal { |
| struct StreamOpOpListTag {}; |
| struct StreamOpDeferredListTag {}; |
| } // namespace internal |
| |
| // class StreamOp. |
| // The library schedules operations, or ops of type StreamOp. An IO operation is a discrete |
| // unit of IO that is meaningful to the client. StreamOps are allocated and freed by the client. |
| // The Scheduler interacts with these via the SchedulerClient interface. A reference to each op |
| // acquired through this interface is retained until the Release() method is called. |
| class StreamOp |
| : public fbl::ContainableBaseClasses< |
| fbl::TaggedDoublyLinkedListable<StreamOp*, internal::StreamOpOpListTag>, |
| fbl::TaggedDoublyLinkedListable<StreamOp*, internal::StreamOpDeferredListTag>> { |
| public: |
| using OpListTag = internal::StreamOpOpListTag; |
| using DeferredListTag = internal::StreamOpDeferredListTag; |
| |
| using OpList = fbl::TaggedDoublyLinkedList<StreamOp*, OpListTag>; |
| using DeferredList = fbl::TaggedDoublyLinkedList<StreamOp*, DeferredListTag>; |
| |
| StreamOp() { StreamOp(OpType::kOpTypeUnknown, 0, kOpGroupNone, 0, nullptr); } |
| |
| StreamOp(OpType type, uint32_t stream_id, uint32_t group_id, uint32_t group_members, void* cookie) |
| : type_(type), |
| stream_id_(stream_id), |
| group_id_(group_id), |
| group_members_(group_members), |
| result_(ZX_OK), |
| cookie_(cookie), |
| flags_(0) {} |
| |
| DISALLOW_COPY_ASSIGN_AND_MOVE(StreamOp); |
| |
| OpType type() { return type_; } |
| void set_type(OpType type) { type_ = type; } |
| |
| uint32_t stream_id() { return stream_id_; } |
| void set_stream_id(uint32_t stream_id) { stream_id_ = stream_id; } |
| |
| uint32_t group() { return group_id_; } |
| void set_group(uint32_t gid) { group_id_ = gid; } |
| |
| uint32_t members() { return group_members_; } |
| void set_members(uint32_t group_members) { group_members_ = group_members; } |
| |
| zx_status_t result() { return result_; } |
| void set_result(zx_status_t result) { result_ = result; } |
| |
| void* cookie() { return cookie_; } |
| void set_cookie(void* cookie) { cookie_ = cookie; } |
| |
| uint32_t flags() { return flags_; } |
| void set_flags(uint32_t flags) { flags_ = flags; } |
| bool is_deferred() { return flags_ & kOpFlagDeferred; } |
| |
| private: |
| OpType type_; // Type of operation. |
| uint32_t stream_id_; // Stream into which this op is queued. |
| uint32_t group_id_; // Group of operations. |
| uint32_t group_members_; // Number of members in the group. |
| zx_status_t result_; // Status code of the released operation. |
| void* cookie_; // User-defined per-op cookie. |
| uint32_t flags_; |
| }; |
| |
| } // namespace ioscheduler |
| |
| #endif // IO_SCHEDULER_STREAM_OP_H_ |