blob: d78a8cfcdf6a954a12bd1c833ba4c6a2d8ba2561 [file] [log] [blame]
// 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_