From 8351ed094e3d77b41248d66037c2eac5d07cae65 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Tue, 25 Aug 2015 16:12:53 -0400 Subject: [PATCH] librbd: added async image rename op Signed-off-by: Jason Dillaman --- src/librbd/ImageWatcher.cc | 8 +- src/librbd/Makefile.am | 2 + src/librbd/internal.cc | 118 +++++++---------- src/librbd/internal.h | 1 + src/librbd/operation/RenameRequest.cc | 175 ++++++++++++++++++++++++++ src/librbd/operation/RenameRequest.h | 83 ++++++++++++ 6 files changed, 309 insertions(+), 78 deletions(-) create mode 100644 src/librbd/operation/RenameRequest.cc create mode 100644 src/librbd/operation/RenameRequest.h diff --git a/src/librbd/ImageWatcher.cc b/src/librbd/ImageWatcher.cc index 6cbab0022173a..62cf77f263074 100644 --- a/src/librbd/ImageWatcher.cc +++ b/src/librbd/ImageWatcher.cc @@ -1142,8 +1142,12 @@ void ImageWatcher::handle_payload(const RenamePayload& payload, ldout(m_image_ctx.cct, 10) << this << " remote rename request: " << payload.image_name << dendl; - // TODO - ::encode(ResponseMessage(-EOPNOTSUPP), *out); + C_SaferCond cond_ctx; + librbd::rename_helper(&m_image_ctx, &cond_ctx, + payload.image_name.c_str()); + + int r = cond_ctx.wait(); + ::encode(ResponseMessage(r), *out); } } diff --git a/src/librbd/Makefile.am b/src/librbd/Makefile.am index ee3b1e829d57a..3bf4122cfd0e7 100644 --- a/src/librbd/Makefile.am +++ b/src/librbd/Makefile.am @@ -34,6 +34,7 @@ librbd_internal_la_SOURCES = \ librbd/object_map/UpdateRequest.cc \ librbd/operation/FlattenRequest.cc \ librbd/operation/RebuildObjectMapRequest.cc \ + librbd/operation/RenameRequest.cc \ librbd/operation/Request.cc \ librbd/operation/ResizeRequest.cc \ librbd/operation/SnapshotCreateRequest.cc \ @@ -99,6 +100,7 @@ noinst_HEADERS += \ librbd/object_map/UpdateRequest.h \ librbd/operation/FlattenRequest.h \ librbd/operation/RebuildObjectMapRequest.h \ + librbd/operation/RenameRequest.h \ librbd/operation/Request.h \ librbd/operation/ResizeRequest.h \ librbd/operation/SnapshotCreateRequest.h \ diff --git a/src/librbd/internal.cc b/src/librbd/internal.cc index 8efec10d2f5cf..86c520e843465 100644 --- a/src/librbd/internal.cc +++ b/src/librbd/internal.cc @@ -32,6 +32,7 @@ #include "librbd/parent_types.h" #include "librbd/operation/FlattenRequest.h" #include "librbd/operation/RebuildObjectMapRequest.h" +#include "librbd/operation/RenameRequest.h" #include "librbd/operation/ResizeRequest.h" #include "librbd/operation/SnapshotCreateRequest.h" #include "librbd/operation/SnapshotProtectRequest.h" @@ -1567,13 +1568,16 @@ int invoke_async_request(ImageCtx *ictx, const std::string& request_type, ldout(cct, 20) << "rename " << &io_ctx << " " << srcname << " -> " << dstname << dendl; - bool old_format; - uint64_t src_size; - int r = detect_format(io_ctx, srcname, &old_format, &src_size); + ImageCtx *ictx = new ImageCtx(srcname, "", "", io_ctx, false); + int r = open_image(ictx); if (r < 0) { - lderr(cct) << "error finding source object: " << cpp_strerror(r) << dendl; + lderr(ictx->cct) << "error opening source image: " << cpp_strerror(r) + << dendl; return r; } + BOOST_SCOPE_EXIT((ictx)) { + close_image(ictx); + } BOOST_SCOPE_EXIT_END r = detect_format(io_ctx, dstname, NULL, NULL); if (r < 0 && r != -ENOENT) { @@ -1586,92 +1590,54 @@ int invoke_async_request(ImageCtx *ictx, const std::string& request_type, return -EEXIST; } - string src_oid = - old_format ? old_header_name(srcname) : id_obj_name(srcname); - string dst_oid = - old_format ? old_header_name(dstname) : id_obj_name(dstname); - - string id; - if (!old_format) { - r = cls_client::get_id(&io_ctx, src_oid, &id); - if (r < 0) { - lderr(cct) << "error reading image id: " << cpp_strerror(r) << dendl; - return r; + if (ictx->test_features(RBD_FEATURE_JOURNALING)) { + r = invoke_async_request(ictx, "rename", true, + boost::bind(&rename_helper, ictx, _1, + dstname), + boost::bind(&ImageWatcher::notify_rename, + ictx->image_watcher, dstname)); + if (r < 0 && r != -EEXIST) { + return r; } - } - - bufferlist databl; - map omap_values; - r = io_ctx.read(src_oid, databl, src_size, 0); - if (r < 0) { - lderr(cct) << "error reading source object: " << src_oid << ": " - << cpp_strerror(r) << dendl; - return r; - } + } else { + RWLock::RLocker owner_lock(ictx->owner_lock); + C_SaferCond cond_ctx; + rename_helper(ictx, &cond_ctx, dstname); - int MAX_READ = 1024; - string last_read = ""; - do { - map outbl; - r = io_ctx.omap_get_vals(src_oid, last_read, MAX_READ, &outbl); + r = cond_ctx.wait(); if (r < 0) { - lderr(cct) << "error reading source object omap values: " - << cpp_strerror(r) << dendl; - return r; + return r; } - omap_values.insert(outbl.begin(), outbl.end()); - if (!outbl.empty()) - last_read = outbl.rbegin()->first; - } while (r == MAX_READ); - - librados::ObjectWriteOperation op; - op.create(true); - op.write_full(databl); - if (!omap_values.empty()) - op.omap_set(omap_values); - r = io_ctx.operate(dst_oid, &op); - if (r < 0) { - lderr(cct) << "error writing destination object: " << dst_oid << ": " - << cpp_strerror(r) << dendl; - return r; } - if (old_format) { - r = tmap_set(io_ctx, dstname); - if (r < 0) { - io_ctx.remove(dst_oid); - lderr(cct) << "couldn't add " << dstname << " to directory: " - << cpp_strerror(r) << dendl; - return r; - } - r = tmap_rm(io_ctx, srcname); - if (r < 0) { - lderr(cct) << "warning: couldn't remove old entry from directory (" - << srcname << ")" << dendl; - } - } else { - r = cls_client::dir_rename_image(&io_ctx, RBD_DIRECTORY, - srcname, dstname, id); - if (r < 0) { - lderr(cct) << "error updating directory: " << cpp_strerror(r) << dendl; - return r; - } + if (ictx->old_format) { + notify_change(ictx->md_ctx, ictx->header_oid, ictx); } + return 0; + } - r = io_ctx.remove(src_oid); - if (r < 0 && r != -ENOENT) { - lderr(cct) << "warning: couldn't remove old source object (" - << src_oid << ")" << dendl; + void rename_helper(ImageCtx *ictx, Context *ctx, const char *dstname) + { + assert(ictx->owner_lock.is_locked()); + if (ictx->test_features(RBD_FEATURE_JOURNALING)) { + assert(!ictx->image_watcher->is_lock_supported() || + ictx->image_watcher->is_lock_owner()); } - if (old_format) { - notify_change(io_ctx, old_header_name(srcname), NULL); + ldout(ictx->cct, 20) << "rename_helper " << ictx << " " << dstname + << dendl; + + int r = ictx_check(ictx, ictx->owner_lock); + if (r < 0) { + ctx->complete(r); + return; } - return 0; + operation::RenameRequest *req = + new operation::RenameRequest(*ictx, ctx, dstname); + req->send(); } - int info(ImageCtx *ictx, image_info_t& info, size_t infosize) { ldout(ictx->cct, 20) << "info " << ictx << dendl; diff --git a/src/librbd/internal.h b/src/librbd/internal.h index 964dde68d21bf..04f5a24e87eac 100644 --- a/src/librbd/internal.h +++ b/src/librbd/internal.h @@ -111,6 +111,7 @@ namespace librbd { int clone(IoCtx& p_ioctx, const char *p_name, const char *p_snap_name, IoCtx& c_ioctx, const char *c_name, ImageOptions& c_opts); int rename(librados::IoCtx& io_ctx, const char *srcname, const char *dstname); + void rename_helper(ImageCtx *ictx, Context *ctx, const char *dstname); int info(ImageCtx *ictx, image_info_t& info, size_t image_size); int get_old_format(ImageCtx *ictx, uint8_t *old); int get_size(ImageCtx *ictx, uint64_t *size); diff --git a/src/librbd/operation/RenameRequest.cc b/src/librbd/operation/RenameRequest.cc new file mode 100644 index 0000000000000..1a9c82da7bd1e --- /dev/null +++ b/src/librbd/operation/RenameRequest.cc @@ -0,0 +1,175 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/operation/RenameRequest.h" +#include "common/dout.h" +#include "common/errno.h" +#include "include/rados/librados.hpp" +#include "librbd/ImageCtx.h" +#include "librbd/internal.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::operation::RenameRequest: " + +namespace librbd { +namespace operation { + +namespace { + +std::ostream& operator<<(std::ostream& os, + const RenameRequest::State& state) { + switch(state) { + case RenameRequest::STATE_READ_SOURCE_HEADER: + os << "READ_SOURCE_HEADER"; + break; + case RenameRequest::STATE_WRITE_DEST_HEADER: + os << "WRITE_DEST_HEADER"; + break; + case RenameRequest::STATE_UPDATE_DIRECTORY: + os << "UPDATE_DIRECTORY"; + break; + case RenameRequest::STATE_REMOVE_SOURCE_HEADER: + os << "REMOVE_SOURCE_HEADER"; + break; + default: + os << "UNKNOWN (" << static_cast(state) << ")"; + break; + } + return os; +} + +} // anonymous namespace + +RenameRequest::RenameRequest(ImageCtx &image_ctx, Context *on_finish, + const std::string &dest_name) + : Request(image_ctx, on_finish), m_dest_name(dest_name), + m_source_oid(image_ctx.old_format ? old_header_name(image_ctx.name) : + id_obj_name(image_ctx.name)), + m_dest_oid(image_ctx.old_format ? old_header_name(dest_name) : + id_obj_name(dest_name)) { +} + +void RenameRequest::send_op() { + send_read_source_header(); +} + +bool RenameRequest::should_complete(int r) { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << ": state=" << m_state << ", " + << "r=" << r << dendl; + r = filter_state_return_code(r); + if (r < 0) { + lderr(cct) << "encountered error: " << cpp_strerror(r) << dendl; + return true; + } + + RWLock::RLocker owner_lock(m_image_ctx.owner_lock); + bool finished = false; + switch (m_state) { + case STATE_READ_SOURCE_HEADER: + send_write_destination_header(); + break; + case STATE_WRITE_DEST_HEADER: + send_update_directory(); + break; + case STATE_UPDATE_DIRECTORY: + send_remove_source_header(); + break; + case STATE_REMOVE_SOURCE_HEADER: + finished = true; + break; + default: + assert(false); + break; + } + return finished; +} + +int RenameRequest::filter_state_return_code(int r) { + CephContext *cct = m_image_ctx.cct; + + if (m_state == STATE_REMOVE_SOURCE_HEADER && r < 0) { + if (r != -ENOENT) { + lderr(cct) << "warning: couldn't remove old source object (" + << m_source_oid << ")" << dendl; + } + return 0; + } + return r; +} + +void RenameRequest::send_read_source_header() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + m_state = STATE_READ_SOURCE_HEADER; + + librados::ObjectReadOperation op; + op.read(0, 0, NULL, NULL); + + // TODO: old code read omap values but there are no omap values on the + // old format header nor the new format id object + librados::AioCompletion *rados_completion = create_callback_completion(); + int r = m_image_ctx.md_ctx.aio_operate(m_source_oid, rados_completion, &op, + &m_header_bl); + assert(r == 0); + rados_completion->release(); +} + +void RenameRequest::send_write_destination_header() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + m_state = STATE_WRITE_DEST_HEADER; + + librados::ObjectWriteOperation op; + op.create(true); + op.write_full(m_header_bl); + + librados::AioCompletion *rados_completion = create_callback_completion(); + int r = m_image_ctx.md_ctx.aio_operate(m_dest_oid, rados_completion, &op); + assert(r == 0); + rados_completion->release(); +} + +void RenameRequest::send_update_directory() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + m_state = STATE_UPDATE_DIRECTORY; + + librados::ObjectWriteOperation op; + if (m_image_ctx.old_format) { + bufferlist cmd_bl; + bufferlist empty_bl; + ::encode(static_cast<__u8>(CEPH_OSD_TMAP_SET), cmd_bl); + ::encode(m_dest_name, cmd_bl); + ::encode(empty_bl, cmd_bl); + ::encode(static_cast<__u8>(CEPH_OSD_TMAP_RM), cmd_bl); + ::encode(m_image_ctx.name, cmd_bl); + op.tmap_update(cmd_bl); + } else { + cls_client::dir_rename_image(&op, m_image_ctx.name, m_dest_name, + m_image_ctx.id); + } + + librados::AioCompletion *rados_completion = create_callback_completion(); + int r = m_image_ctx.md_ctx.aio_operate(RBD_DIRECTORY, rados_completion, &op); + assert(r == 0); + rados_completion->release(); +} + +void RenameRequest::send_remove_source_header() { + CephContext *cct = m_image_ctx.cct; + ldout(cct, 5) << this << " " << __func__ << dendl; + m_state = STATE_REMOVE_SOURCE_HEADER; + + librados::ObjectWriteOperation op; + op.remove(); + + librados::AioCompletion *rados_completion = create_callback_completion(); + int r = m_image_ctx.md_ctx.aio_operate(m_source_oid, rados_completion, &op); + assert(r == 0); + rados_completion->release(); +} + +} // namespace operation +} // namespace librbd diff --git a/src/librbd/operation/RenameRequest.h b/src/librbd/operation/RenameRequest.h new file mode 100644 index 0000000000000..b07293facff49 --- /dev/null +++ b/src/librbd/operation/RenameRequest.h @@ -0,0 +1,83 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_RENAME_REQUEST_HH +#define CEPH_LIBRBD_RENAME_REQUEST_H + +#include "librbd/operation/Request.h" +#include +#include + +class Context; + +namespace librbd { + +class ImageCtx; + +namespace operation { + +class RenameRequest : public Request +{ +public: + /** + * Rename goes through the following state machine: + * + * @verbatim + * + * + * | + * v + * STATE_READ_SOURCE_HEADER + * | + * v + * STATE_WRITE_DEST_HEADER + * | + * v + * STATE_UPDATE_DIRECTORY + * | + * v + * STATE_REMOVE_SOURCE_HEADER + * | + * v + * + * + * @endverbatim + * + */ + enum State { + STATE_READ_SOURCE_HEADER, + STATE_WRITE_DEST_HEADER, + STATE_UPDATE_DIRECTORY, + STATE_REMOVE_SOURCE_HEADER + }; + + RenameRequest(ImageCtx &image_ctx, Context *on_finish, + const std::string &dest_name); + +protected: + virtual void send_op(); + virtual bool should_complete(int r); + +private: + std::string m_dest_name; + + std::string m_source_oid; + std::string m_dest_oid; + + State m_state; + + bufferlist m_header_bl; + + int filter_state_return_code(int r); + + void send_read_source_header(); + void send_write_destination_header(); + void send_update_directory(); + void send_remove_source_header(); + +}; + +} // namespace operation +} // namespace librbd + +#endif // CEPH_LIBRBD_RENAME_REQUEST_H -- 2.39.5