#include "common/errno.h"
#include "common/Cond.h"
#include "cls/rbd/cls_rbd_client.h"
+#include "librbd/AsioEngine.h"
#include "librbd/DeepCopyRequest.h"
#include "librbd/ExclusiveLock.h"
#include "librbd/ImageCtx.h"
#include "librbd/ImageState.h"
#include "librbd/internal.h"
+#include "librbd/Operations.h"
#include "librbd/Utils.h"
#include "librbd/api/Config.h"
#include "librbd/api/Trash.h"
+#include "librbd/api/Utils.h"
+#include "librbd/crypto/FormatRequest.h"
+#include "librbd/crypto/LoadRequest.h"
+#include "librbd/deep_copy/Handler.h"
#include "librbd/image/CloneRequest.h"
#include "librbd/image/RemoveRequest.h"
#include "librbd/image/PreRemoveRequest.h"
+#include "librbd/io/ImageDispatcherInterface.h"
+#include "librbd/io/ObjectDispatcherInterface.h"
+#include "librbd/io/AioCompletion.h"
+#include "librbd/io/ImageDispatchSpec.h"
#include <boost/scope_exit.hpp>
#define dout_subsys ceph_subsys_rbd
#undef dout_prefix
#define dout_prefix *_dout << "librbd::api::Image: " << __func__ << ": "
+using std::map;
+using std::string;
+using librados::snap_t;
+
namespace librbd {
namespace api {
return r;
}
- RWLock::RLocker snap_locker(ictx->snap_lock);
+ std::shared_lock image_locker{ictx->image_lock};
*op_features = ictx->op_features;
return 0;
}
return r;
}
- RWLock::RLocker snap_locker(ictx->snap_lock);
- RWLock::RLocker parent_locker(ictx->parent_lock);
+ std::shared_lock image_locker{ictx->image_lock};
- bool release_parent_locks = false;
- BOOST_SCOPE_EXIT_ALL(ictx, &release_parent_locks) {
- if (release_parent_locks) {
- ictx->parent->parent_lock.put_read();
- ictx->parent->snap_lock.put_read();
+ bool release_image_lock = false;
+ BOOST_SCOPE_EXIT_ALL(ictx, &release_image_lock) {
+ if (release_image_lock) {
+ ictx->parent->image_lock.unlock_shared();
}
};
// of the migration source image
auto parent = ictx->parent;
if (!ictx->migration_info.empty() && ictx->parent != nullptr) {
- release_parent_locks = true;
- ictx->parent->snap_lock.get_read();
- ictx->parent->parent_lock.get_read();
+ release_image_lock = true;
+ ictx->parent->image_lock.lock_shared();
parent = ictx->parent->parent;
}
parent_image->pool_name = parent->md_ctx.get_pool_name();
parent_image->pool_namespace = parent->md_ctx.get_namespace();
- RWLock::RLocker parent_snap_locker(parent->snap_lock);
+ std::shared_lock parent_image_locker{parent->image_lock};
parent_snap->id = parent->snap_id;
parent_snap->namespace_type = RBD_SNAP_NAMESPACE_TYPE_USER;
if (parent->snap_id != CEPH_NOSNAP) {
std::vector<librbd::linked_image_spec_t> *images) {
ImageCtx *ictx = new librbd::ImageCtx("", image_id, nullptr,
io_ctx, true);
+ CephContext *cct = ictx->cct;
int r = ictx->state->open(OPEN_FLAG_SKIP_OPEN_PARENT);
if (r < 0) {
if (r == -ENOENT) {
return 0;
}
- lderr(ictx->cct) << "failed to open descendant " << image_id
- << " from pool " << io_ctx.get_pool_name() << ":"
- << cpp_strerror(r) << dendl;
+ lderr(cct) << "failed to open descendant " << image_id
+ << " from pool " << io_ctx.get_pool_name() << ":"
+ << cpp_strerror(r) << dendl;
return r;
}
int r1 = ictx->state->close();
if (r1 < 0) {
- lderr(ictx->cct) << "error when closing descendant " << image_id
- << " from pool " << io_ctx.get_pool_name() << ":"
- << cpp_strerror(r) << dendl;
+ lderr(cct) << "error when closing descendant " << image_id
+ << " from pool " << io_ctx.get_pool_name() << ":"
+ << cpp_strerror(r1) << dendl;
}
return r;
int Image<I>::list_descendants(
I *ictx, const std::optional<size_t> &max_level,
std::vector<librbd::linked_image_spec_t> *images) {
- RWLock::RLocker l(ictx->snap_lock);
+ std::shared_lock l{ictx->image_lock};
std::vector<librados::snap_t> snap_ids;
if (ictx->snap_id != CEPH_NOSNAP) {
snap_ids.push_back(ictx->snap_id);
ldout(cct, 20) << "ictx=" << ictx << dendl;
// no children for non-layered or old format image
- if (!ictx->test_features(RBD_FEATURE_LAYERING, ictx->snap_lock)) {
+ if (!ictx->test_features(RBD_FEATURE_LAYERING, ictx->image_lock)) {
return 0;
}
}
IoCtx ioctx;
- r = util::create_ioctx(ictx->md_ctx, "child image", it.first, {}, &ioctx);
+ r = librbd::util::create_ioctx(
+ ictx->md_ctx, "child image", it.first, {}, &ioctx);
if (r == -ENOENT) {
continue;
} else if (r < 0) {
for (auto& image_id : image_ids) {
images->push_back({
it.first, "", ictx->md_ctx.get_namespace(), image_id, "", false});
- r = list_descendants(ictx->md_ctx, image_id, child_max_level, images);
+ r = list_descendants(ioctx, image_id, child_max_level, images);
if (r < 0) {
return r;
}
// retrieve clone v2 children attached to this snapshot
IoCtx parent_io_ctx;
- r = util::create_ioctx(ictx->md_ctx, "parent image", parent_spec.pool_id,
- parent_spec.pool_namespace, &parent_io_ctx);
+ r = librbd::util::create_ioctx(
+ ictx->md_ctx, "parent image",parent_spec.pool_id,
+ parent_spec.pool_namespace, &parent_io_ctx);
if (r < 0) {
return r;
}
cls::rbd::ChildImageSpecs child_images;
- r = cls_client::children_list(&parent_io_ctx,
- util::header_name(parent_spec.image_id),
- parent_spec.snap_id, &child_images);
+ r = cls_client::children_list(
+ &parent_io_ctx, librbd::util::header_name(parent_spec.image_id),
+ parent_spec.snap_id, &child_images);
if (r < 0 && r != -ENOENT && r != -EOPNOTSUPP) {
lderr(cct) << "error retrieving children: " << cpp_strerror(r) << dendl;
return r;
child_image.image_id, "", false});
if (!child_max_level || *child_max_level > 0) {
IoCtx ioctx;
- r = util::create_ioctx(ictx->md_ctx, "child image", child_image.pool_id,
- child_image.pool_namespace, &ioctx);
+ r = librbd::util::create_ioctx(
+ ictx->md_ctx, "child image", child_image.pool_id,
+ child_image.pool_namespace, &ioctx);
if (r == -ENOENT) {
continue;
} else if (r < 0) {
for (auto& image : *images) {
if (child_pool_id == -1 || child_pool_id != image.pool_id ||
child_io_ctx.get_namespace() != image.pool_namespace) {
- r = util::create_ioctx(ictx->md_ctx, "child image", image.pool_id,
- image.pool_namespace, &child_io_ctx);
- if (r < 0) {
+ r = librbd::util::create_ioctx(
+ ictx->md_ctx, "child image", image.pool_id, image.pool_namespace,
+ &child_io_ctx);
+ if (r == -ENOENT) {
+ image.pool_name = "";
+ image.image_name = "";
+ continue;
+ } else if (r < 0) {
return r;
}
child_pool_id = image.pool_id;
uint64_t features;
uint64_t src_size;
{
- RWLock::RLocker snap_locker(src->snap_lock);
+ std::shared_lock image_locker{src->image_lock};
if (!src->migration_info.empty()) {
lderr(cct) << "cannot deep copy migrating image" << dendl;
return -EBUSY;
}
- features = (src->features & ~RBD_FEATURES_IMPLICIT_ENABLE);
+ features = src->features;
src_size = src->get_image_size(src->snap_id);
}
uint64_t format = 2;
if (flatten > 0) {
parent_spec.pool_id = -1;
} else {
- RWLock::RLocker snap_locker(src->snap_lock);
- RWLock::RLocker parent_locker(src->parent_lock);
+ std::shared_lock image_locker{src->image_lock};
// use oldest snapshot or HEAD for parent spec
if (!src->snap_info.empty()) {
r = create(dest_md_ctx, destname, "", src_size, opts, "", "", false);
} else {
librados::IoCtx parent_io_ctx;
- r = util::create_ioctx(src->md_ctx, "parent image", parent_spec.pool_id,
- parent_spec.pool_namespace, &parent_io_ctx);
+ r = librbd::util::create_ioctx(
+ src->md_ctx, "parent image", parent_spec.pool_id,
+ parent_spec.pool_namespace, &parent_io_ctx);
if (r < 0) {
return r;
}
api::Config<I>::apply_pool_overrides(dest_md_ctx, &config);
C_SaferCond ctx;
- std::string dest_id = util::generate_image_id(dest_md_ctx);
+ std::string dest_id = librbd::util::generate_image_id(dest_md_ctx);
auto *req = image::CloneRequest<I>::create(
- config, parent_io_ctx, parent_spec.image_id, "", parent_spec.snap_id,
- dest_md_ctx, destname, dest_id, opts, "", "", src->op_work_queue, &ctx);
+ config, parent_io_ctx, parent_spec.image_id, "", {}, parent_spec.snap_id,
+ dest_md_ctx, destname, dest_id, opts, cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
+ "", "", src->op_work_queue, &ctx);
req->send();
r = ctx.wait();
}
C_SaferCond lock_ctx;
{
- RWLock::WLocker locker(dest->owner_lock);
+ std::unique_lock locker{dest->owner_lock};
if (dest->exclusive_lock == nullptr ||
dest->exclusive_lock->is_lock_owner()) {
template <typename I>
int Image<I>::deep_copy(I *src, I *dest, bool flatten,
ProgressContext &prog_ctx) {
- CephContext *cct = src->cct;
+ // ensure previous writes are visible to dest
+ C_SaferCond flush_ctx;
+ {
+ std::shared_lock owner_locker{src->owner_lock};
+ auto aio_comp = io::AioCompletion::create_and_start(&flush_ctx, src,
+ io::AIO_TYPE_FLUSH);
+ auto req = io::ImageDispatchSpec::create_flush(
+ *src, io::IMAGE_DISPATCH_LAYER_INTERNAL_START,
+ aio_comp, io::FLUSH_SOURCE_INTERNAL, {});
+ req->send();
+ }
+ int r = flush_ctx.wait();
+ if (r < 0) {
+ return r;
+ }
+
librados::snap_t snap_id_start = 0;
librados::snap_t snap_id_end;
{
- RWLock::RLocker snap_locker(src->snap_lock);
+ std::shared_lock image_locker{src->image_lock};
snap_id_end = src->snap_id;
}
- ThreadPool *thread_pool;
- ContextWQ *op_work_queue;
- ImageCtx::get_thread_pool_instance(cct, &thread_pool, &op_work_queue);
+ AsioEngine asio_engine(src->md_ctx);
C_SaferCond cond;
SnapSeqs snap_seqs;
- auto req = DeepCopyRequest<I>::create(src, dest, snap_id_start, snap_id_end,
- flatten, boost::none, op_work_queue,
- &snap_seqs, &prog_ctx, &cond);
+ deep_copy::ProgressHandler progress_handler{&prog_ctx};
+ auto req = DeepCopyRequest<I>::create(
+ src, dest, snap_id_start, snap_id_end, 0U, flatten, boost::none,
+ asio_engine.get_work_queue(), &snap_seqs, &progress_handler, &cond);
req->send();
- int r = cond.wait();
+ r = cond.wait();
if (r < 0) {
return r;
}
uint64_t snap_id = CEPH_NOSNAP;
std::string name(snap_name == nullptr ? "" : snap_name);
if (!name.empty()) {
- RWLock::RLocker snap_locker(ictx->snap_lock);
- snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace{},
- snap_name);
+ std::shared_lock image_locker{ictx->image_lock};
+ snap_id = ictx->get_snap_id(snap_namespace, snap_name);
if (snap_id == CEPH_NOSNAP) {
return -ENOENT;
}
r = Trash<I>::list(io_ctx, trash_entries, false);
if (r < 0) {
return r;
- } else if (r >= 0) {
- for (auto& entry : trash_entries) {
- if (entry.name == image_name &&
- entry.source == RBD_TRASH_IMAGE_SOURCE_REMOVING) {
- return Trash<I>::remove(io_ctx, entry.id, true, prog_ctx);
+ }
+ for (auto& entry : trash_entries) {
+ if (entry.name == image_name &&
+ entry.source == RBD_TRASH_IMAGE_SOURCE_REMOVING) {
+ cls::rbd::TrashImageSpec spec;
+ r = cls_client::trash_get(&io_ctx, entry.id, &spec);
+ if (r < 0) {
+ lderr(cct) << "error getting image id " << entry.id
+ << " info from trash: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+ if (spec.state == cls::rbd::TRASH_IMAGE_STATE_MOVING) {
+ r = Trash<I>::move(io_ctx, entry.source, entry.name, entry.id, 0);
+ if (r < 0) {
+ return r;
+ }
}
+ return Trash<I>::remove(io_ctx, entry.id, true, prog_ctx);
}
}
// attempt to pre-validate the removal before moving to trash and
// removing
r = pre_remove_image<I>(io_ctx, image_id);
- if (r < 0 && r != -ENOENT) {
+ if (r == -ECHILD) {
+ if (config.get_val<bool>("rbd_move_parent_to_trash_on_remove")) {
+ // keep the image in the trash until the last child is removed
+ trash_image_source = RBD_TRASH_IMAGE_SOURCE_USER_PARENT;
+ } else {
+ lderr(cct) << "image has snapshots - not removing" << dendl;
+ return -ENOTEMPTY;
+ }
+ } else if (r < 0 && r != -ENOENT) {
return r;
}
}
// fall-through if trash isn't supported
}
- ThreadPool *thread_pool;
- ContextWQ *op_work_queue;
- ImageCtx::get_thread_pool_instance(cct, &thread_pool, &op_work_queue);
+ AsioEngine asio_engine(io_ctx);
// might be a V1 image format that cannot be moved to the trash
// and would not have been listed in the V2 directory -- or the OSDs
// are too old and don't support the trash feature
C_SaferCond cond;
auto req = librbd::image::RemoveRequest<I>::create(
- io_ctx, image_name, "", false, false, prog_ctx, op_work_queue, &cond);
+ io_ctx, image_name, "", false, false, prog_ctx,
+ asio_engine.get_work_queue(), &cond);
req->send();
return cond.wait();
}
+template <typename I>
+int Image<I>::flatten_children(I *ictx, const char* snap_name,
+ ProgressContext& pctx) {
+ CephContext *cct = ictx->cct;
+ ldout(cct, 20) << "children flatten " << ictx->name << dendl;
+
+ int r = ictx->state->refresh_if_required();
+ if (r < 0) {
+ return r;
+ }
+
+ std::shared_lock l{ictx->image_lock};
+ snap_t snap_id = ictx->get_snap_id(cls::rbd::UserSnapshotNamespace(),
+ snap_name);
+
+ cls::rbd::ParentImageSpec parent_spec{ictx->md_ctx.get_id(),
+ ictx->md_ctx.get_namespace(),
+ ictx->id, snap_id};
+ std::vector<librbd::linked_image_spec_t> child_images;
+ r = list_children(ictx, parent_spec, &child_images);
+ if (r < 0) {
+ return r;
+ }
+
+ size_t size = child_images.size();
+ if (size == 0) {
+ return 0;
+ }
+
+ librados::IoCtx child_io_ctx;
+ int64_t child_pool_id = -1;
+ size_t i = 0;
+ for (auto &child_image : child_images){
+ std::string pool = child_image.pool_name;
+ if (child_pool_id == -1 ||
+ child_pool_id != child_image.pool_id ||
+ child_io_ctx.get_namespace() != child_image.pool_namespace) {
+ r = librbd::util::create_ioctx(
+ ictx->md_ctx, "child image", child_image.pool_id,
+ child_image.pool_namespace, &child_io_ctx);
+ if (r < 0) {
+ return r;
+ }
+
+ child_pool_id = child_image.pool_id;
+ }
+
+ ImageCtx *imctx = new ImageCtx("", child_image.image_id, nullptr,
+ child_io_ctx, false);
+ r = imctx->state->open(0);
+ if (r < 0) {
+ lderr(cct) << "error opening image: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ if ((imctx->features & RBD_FEATURE_DEEP_FLATTEN) == 0 &&
+ !imctx->snaps.empty()) {
+ lderr(cct) << "snapshot in-use by " << pool << "/" << imctx->name
+ << dendl;
+ imctx->state->close();
+ return -EBUSY;
+ }
+
+ librbd::NoOpProgressContext prog_ctx;
+ r = imctx->operations->flatten(prog_ctx);
+ if (r < 0) {
+ lderr(cct) << "error flattening image: " << pool << "/"
+ << (child_image.pool_namespace.empty() ?
+ "" : "/" + child_image.pool_namespace)
+ << child_image.image_name << cpp_strerror(r) << dendl;
+ imctx->state->close();
+ return r;
+ }
+
+ r = imctx->state->close();
+ if (r < 0) {
+ lderr(cct) << "failed to close image: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ pctx.update_progress(++i, size);
+ ceph_assert(i <= size);
+ }
+
+ return 0;
+}
+
+template <typename I>
+int Image<I>::encryption_format(I* ictx, encryption_format_t format,
+ encryption_options_t opts, size_t opts_size,
+ bool c_api) {
+ if (ictx->parent != nullptr) {
+ lderr(ictx->cct) << "cannot format a cloned image" << dendl;
+ return -ENOTSUP;
+ }
+
+ crypto::EncryptionFormat<I>* result_format;
+ auto r = util::create_encryption_format(
+ ictx->cct, format, opts, opts_size, c_api, &result_format);
+ if (r != 0) {
+ return r;
+ }
+
+ C_SaferCond cond;
+ auto req = librbd::crypto::FormatRequest<I>::create(
+ ictx, std::unique_ptr<crypto::EncryptionFormat<I>>(result_format),
+ &cond);
+ req->send();
+ return cond.wait();
+}
+
+template <typename I>
+int Image<I>::encryption_load(I* ictx, encryption_format_t format,
+ encryption_options_t opts, size_t opts_size,
+ bool c_api) {
+ crypto::EncryptionFormat<I>* result_format;
+ auto r = util::create_encryption_format(
+ ictx->cct, format, opts, opts_size, c_api, &result_format);
+ if (r != 0) {
+ return r;
+ }
+
+ C_SaferCond cond;
+ auto req = librbd::crypto::LoadRequest<I>::create(
+ ictx, std::unique_ptr<crypto::EncryptionFormat<I>>(result_format),
+ &cond);
+ req->send();
+ return cond.wait();
+}
+
} // namespace api
} // namespace librbd