// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #ifndef CEPH_LIBRBD_IO_OBJECT_REQUEST_H #define CEPH_LIBRBD_IO_OBJECT_REQUEST_H #include "include/int_types.h" #include "include/buffer.h" #include "include/neorados/RADOS.hpp" #include "include/rados/librados.hpp" #include "common/zipkin_trace.h" #include "librbd/ObjectMap.h" #include "librbd/Types.h" #include "librbd/io/Types.h" #include class Context; class ObjectExtent; namespace neorados { struct WriteOp; } namespace librbd { struct ImageCtx; namespace io { struct AioCompletion; template class CopyupRequest; /** * This class represents an I/O operation to a single RBD data object. * Its subclasses encapsulate logic for dealing with special cases * for I/O due to layering. */ template class ObjectRequest { public: static ObjectRequest* create_write( ImageCtxT *ictx, uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, IOContext io_context, int op_flags, int write_flags, std::optional assert_version, const ZTracer::Trace &parent_trace, Context *completion); static ObjectRequest* create_discard( ImageCtxT *ictx, uint64_t object_no, uint64_t object_off, uint64_t object_len, IOContext io_context, int discard_flags, const ZTracer::Trace &parent_trace, Context *completion); static ObjectRequest* create_write_same( ImageCtxT *ictx, uint64_t object_no, uint64_t object_off, uint64_t object_len, ceph::bufferlist&& data, IOContext io_context, int op_flags, const ZTracer::Trace &parent_trace, Context *completion); static ObjectRequest* create_compare_and_write( ImageCtxT *ictx, uint64_t object_no, uint64_t object_off, ceph::bufferlist&& cmp_data, ceph::bufferlist&& write_data, IOContext io_context, uint64_t *mismatch_offset, int op_flags, const ZTracer::Trace &parent_trace, Context *completion); ObjectRequest(ImageCtxT *ictx, uint64_t objectno, IOContext io_context, const char *trace_name, const ZTracer::Trace &parent_trace, Context *completion); virtual ~ObjectRequest() { m_trace.event("finish"); } static void add_write_hint(ImageCtxT& image_ctx, neorados::WriteOp *wr); virtual void send() = 0; bool has_parent() const { return m_has_parent; } virtual const char *get_op_type() const = 0; protected: bool compute_parent_extents(Extents *parent_extents, bool read_request); ImageCtxT *m_ictx; uint64_t m_object_no; IOContext m_io_context; Context *m_completion; ZTracer::Trace m_trace; void async_finish(int r); void finish(int r); private: bool m_has_parent = false; }; template class ObjectReadRequest : public ObjectRequest { public: static ObjectReadRequest* create( ImageCtxT *ictx, uint64_t objectno, ReadExtents* extents, IOContext io_context, int op_flags, int read_flags, const ZTracer::Trace &parent_trace, uint64_t* version, Context *completion) { return new ObjectReadRequest(ictx, objectno, extents, io_context, op_flags, read_flags, parent_trace, version, completion); } ObjectReadRequest( ImageCtxT *ictx, uint64_t objectno, ReadExtents* extents, IOContext io_context, int op_flags, int read_flags, const ZTracer::Trace &parent_trace, uint64_t* version, Context *completion); void send() override; const char *get_op_type() const override { return "read"; } private: /** * @verbatim * * * | * | * v * READ_OBJECT * | * v (skip if not needed) * READ_PARENT * | * v (skip if not needed) * COPYUP * | * v * * * @endverbatim */ ReadExtents* m_extents; int m_op_flags; int m_read_flags; uint64_t* m_version; void read_object(); void handle_read_object(int r); void read_parent(); void handle_read_parent(int r); void copyup(); }; template class AbstractObjectWriteRequest : public ObjectRequest { public: AbstractObjectWriteRequest( ImageCtxT *ictx, uint64_t object_no, uint64_t object_off, uint64_t len, IOContext io_context, const char *trace_name, const ZTracer::Trace &parent_trace, Context *completion); virtual bool is_empty_write_op() const { return false; } virtual uint8_t get_pre_write_object_map_state() const { return OBJECT_EXISTS; } virtual void add_copyup_ops(neorados::WriteOp *wr) { add_write_ops(wr); } void handle_copyup(int r); void send() override; protected: uint64_t m_object_off; uint64_t m_object_len; bool m_full_object = false; bool m_copyup_enabled = true; virtual bool is_no_op_for_nonexistent_object() const { return false; } virtual bool is_object_map_update_enabled() const { return true; } virtual bool is_post_copyup_write_required() const { return false; } virtual bool is_non_existent_post_write_object_map_state() const { return false; } virtual void add_write_hint(neorados::WriteOp *wr); virtual void add_write_ops(neorados::WriteOp *wr) = 0; virtual int filter_write_result(int r) const { return r; } virtual Extents get_copyup_overwrite_extents() const { return {{m_object_off, m_object_len}}; } private: /** * @verbatim * * * | * v (no-op write request) * DETECT_NO_OP . . . . . . . . . . . . . . . . . . . * | . * v (skip if not required/disabled) . * PRE_UPDATE_OBJECT_MAP . * | . . * | . (child dne) . * | . . . . . . . . . . * | . . * | (post-copyup write) . . * | . . . . . . . . . . . . . . * | . . . . * v v . v . * WRITE . . . . . . . . > COPYUP (if required) . * | | . * |/----------------------/ . * | . * v (skip if not required/disabled) . * POST_UPDATE_OBJECT_MAP . * | . * v . * < . . . . . . . . . . . . . . . . . . . . * * @endverbatim */ Extents m_parent_extents; bool m_object_may_exist = false; bool m_copyup_in_progress = false; bool m_guarding_migration_write = false; void compute_parent_info(); void pre_write_object_map_update(); void handle_pre_write_object_map_update(int r); void write_object(); void handle_write_object(int r); void copyup(); void post_write_object_map_update(); void handle_post_write_object_map_update(int r); }; template class ObjectWriteRequest : public AbstractObjectWriteRequest { public: ObjectWriteRequest( ImageCtxT *ictx, uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, IOContext io_context, int op_flags, int write_flags, std::optional assert_version, const ZTracer::Trace &parent_trace, Context *completion) : AbstractObjectWriteRequest(ictx, object_no, object_off, data.length(), io_context, "write", parent_trace, completion), m_write_data(std::move(data)), m_op_flags(op_flags), m_write_flags(write_flags), m_assert_version(assert_version) { } bool is_empty_write_op() const override { return (m_write_data.length() == 0); } const char *get_op_type() const override { return "write"; } protected: void add_write_ops(neorados::WriteOp *wr) override; void add_write_hint(neorados::WriteOp *wr) override; private: ceph::bufferlist m_write_data; int m_op_flags; int m_write_flags; std::optional m_assert_version; }; template class ObjectDiscardRequest : public AbstractObjectWriteRequest { public: ObjectDiscardRequest( ImageCtxT *ictx, uint64_t object_no, uint64_t object_off, uint64_t object_len, IOContext io_context, int discard_flags, const ZTracer::Trace &parent_trace, Context *completion) : AbstractObjectWriteRequest(ictx, object_no, object_off, object_len, io_context, "discard", parent_trace, completion), m_discard_flags(discard_flags) { if (this->m_full_object) { if ((m_discard_flags & OBJECT_DISCARD_FLAG_DISABLE_CLONE_REMOVE) != 0 && this->has_parent()) { if (!this->m_copyup_enabled) { // need to hide the parent object instead of child object m_discard_action = DISCARD_ACTION_REMOVE_TRUNCATE; } else { m_discard_action = DISCARD_ACTION_TRUNCATE; } } else { m_discard_action = DISCARD_ACTION_REMOVE; } } else if (object_off + object_len == ictx->layout.object_size) { m_discard_action = DISCARD_ACTION_TRUNCATE; } else { m_discard_action = DISCARD_ACTION_ZERO; } } const char* get_op_type() const override { switch (m_discard_action) { case DISCARD_ACTION_REMOVE: return "remove"; case DISCARD_ACTION_REMOVE_TRUNCATE: return "remove (create+truncate)"; case DISCARD_ACTION_TRUNCATE: return "truncate"; case DISCARD_ACTION_ZERO: return "zero"; } ceph_abort(); return nullptr; } uint8_t get_pre_write_object_map_state() const override { if (m_discard_action == DISCARD_ACTION_REMOVE) { return OBJECT_PENDING; } return OBJECT_EXISTS; } protected: bool is_no_op_for_nonexistent_object() const override { return (!this->has_parent()); } bool is_object_map_update_enabled() const override { return ( (m_discard_flags & OBJECT_DISCARD_FLAG_DISABLE_OBJECT_MAP_UPDATE) == 0); } bool is_non_existent_post_write_object_map_state() const override { return (m_discard_action == DISCARD_ACTION_REMOVE); } void add_write_hint(neorados::WriteOp *wr) override { // no hint for discard } void add_write_ops(neorados::WriteOp *wr) override; private: enum DiscardAction { DISCARD_ACTION_REMOVE, DISCARD_ACTION_REMOVE_TRUNCATE, DISCARD_ACTION_TRUNCATE, DISCARD_ACTION_ZERO }; DiscardAction m_discard_action; int m_discard_flags; }; template class ObjectWriteSameRequest : public AbstractObjectWriteRequest { public: ObjectWriteSameRequest( ImageCtxT *ictx, uint64_t object_no, uint64_t object_off, uint64_t object_len, ceph::bufferlist&& data, IOContext io_context, int op_flags, const ZTracer::Trace &parent_trace, Context *completion) : AbstractObjectWriteRequest(ictx, object_no, object_off, object_len, io_context, "writesame", parent_trace, completion), m_write_data(std::move(data)), m_op_flags(op_flags) { } const char *get_op_type() const override { return "writesame"; } protected: void add_write_ops(neorados::WriteOp *wr) override; private: ceph::bufferlist m_write_data; int m_op_flags; }; template class ObjectCompareAndWriteRequest : public AbstractObjectWriteRequest { public: ObjectCompareAndWriteRequest( ImageCtxT *ictx, uint64_t object_no, uint64_t object_off, ceph::bufferlist&& cmp_bl, ceph::bufferlist&& write_bl, IOContext io_context, uint64_t *mismatch_offset, int op_flags, const ZTracer::Trace &parent_trace, Context *completion) : AbstractObjectWriteRequest(ictx, object_no, object_off, cmp_bl.length(), io_context, "compare_and_write", parent_trace, completion), m_cmp_bl(std::move(cmp_bl)), m_write_bl(std::move(write_bl)), m_mismatch_offset(mismatch_offset), m_op_flags(op_flags) { } const char *get_op_type() const override { return "compare_and_write"; } void add_copyup_ops(neorados::WriteOp *wr) override { // no-op on copyup } protected: virtual bool is_post_copyup_write_required() const { return true; } void add_write_ops(neorados::WriteOp *wr) override; int filter_write_result(int r) const override; Extents get_copyup_overwrite_extents() const override { return {}; } private: ceph::bufferlist m_cmp_bl; ceph::bufferlist m_write_bl; uint64_t *m_mismatch_offset; int m_op_flags; }; template class ObjectListSnapsRequest : public ObjectRequest { public: static ObjectListSnapsRequest* create( ImageCtxT *ictx, uint64_t objectno, Extents&& object_extents, SnapIds&& snap_ids, int list_snaps_flags, const ZTracer::Trace &parent_trace, SnapshotDelta* snapshot_delta, Context *completion) { return new ObjectListSnapsRequest(ictx, objectno, std::move(object_extents), std::move(snap_ids), list_snaps_flags, parent_trace, snapshot_delta, completion); } ObjectListSnapsRequest( ImageCtxT *ictx, uint64_t objectno, Extents&& object_extents, SnapIds&& snap_ids, int list_snaps_flags, const ZTracer::Trace &parent_trace, SnapshotDelta* snapshot_delta, Context *completion); void send() override; const char *get_op_type() const override { return "snap_list"; } private: Extents m_object_extents; SnapIds m_snap_ids; int m_list_snaps_flags; SnapshotDelta* m_snapshot_delta; neorados::SnapSet m_snap_set; boost::system::error_code m_ec; SnapshotDelta m_parent_snapshot_delta; void list_snaps(); void handle_list_snaps(int r); void list_from_parent(); void handle_list_from_parent(int r); void zero_extent(uint64_t snap_id, bool dne); }; } // namespace io } // namespace librbd extern template class librbd::io::ObjectRequest; extern template class librbd::io::ObjectReadRequest; extern template class librbd::io::AbstractObjectWriteRequest; extern template class librbd::io::ObjectWriteRequest; extern template class librbd::io::ObjectDiscardRequest; extern template class librbd::io::ObjectWriteSameRequest; extern template class librbd::io::ObjectCompareAndWriteRequest; extern template class librbd::io::ObjectListSnapsRequest; #endif // CEPH_LIBRBD_IO_OBJECT_REQUEST_H