// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "librbd/image/OpenRequest.h" #include "common/dout.h" #include "common/errno.h" #include "cls/rbd/cls_rbd_client.h" #include "librbd/ConfigWatcher.h" #include "librbd/ImageCtx.h" #include "librbd/PluginRegistry.h" #include "librbd/Utils.h" #include "librbd/cache/ObjectCacherObjectDispatch.h" #include "librbd/cache/WriteAroundObjectDispatch.h" #include "librbd/image/CloseRequest.h" #include "librbd/image/RefreshRequest.h" #include "librbd/image/SetSnapRequest.h" #include "librbd/io/SimpleSchedulerObjectDispatch.h" #include #include "include/ceph_assert.h" #define dout_subsys ceph_subsys_rbd #undef dout_prefix #define dout_prefix *_dout << "librbd::image::OpenRequest: " namespace librbd { namespace image { using util::create_context_callback; using util::create_rados_callback; template OpenRequest::OpenRequest(I *image_ctx, uint64_t flags, Context *on_finish) : m_image_ctx(image_ctx), m_skip_open_parent_image(flags & OPEN_FLAG_SKIP_OPEN_PARENT), m_on_finish(on_finish), m_error_result(0) { if ((flags & OPEN_FLAG_OLD_FORMAT) != 0) { m_image_ctx->old_format = true; } if ((flags & OPEN_FLAG_IGNORE_MIGRATING) != 0) { m_image_ctx->ignore_migrating = true; } } template void OpenRequest::send() { if (m_image_ctx->old_format) { send_v1_detect_header(); } else { send_v2_detect_header(); } } template void OpenRequest::send_v1_detect_header() { librados::ObjectReadOperation op; op.stat(NULL, NULL, NULL); using klass = OpenRequest; librados::AioCompletion *comp = create_rados_callback(this); m_out_bl.clear(); m_image_ctx->md_ctx.aio_operate(util::old_header_name(m_image_ctx->name), comp, &op, &m_out_bl); comp->release(); } template Context *OpenRequest::handle_v1_detect_header(int *result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; if (*result < 0) { if (*result != -ENOENT) { lderr(cct) << "failed to stat image header: " << cpp_strerror(*result) << dendl; } send_close_image(*result); } else { ldout(cct, 1) << "RBD image format 1 is deprecated. " << "Please copy this image to image format 2." << dendl; m_image_ctx->old_format = true; m_image_ctx->header_oid = util::old_header_name(m_image_ctx->name); m_image_ctx->apply_metadata({}, true); send_refresh(); } return nullptr; } template void OpenRequest::send_v2_detect_header() { if (m_image_ctx->id.empty()) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << dendl; librados::ObjectReadOperation op; op.stat(NULL, NULL, NULL); using klass = OpenRequest; librados::AioCompletion *comp = create_rados_callback(this); m_out_bl.clear(); m_image_ctx->md_ctx.aio_operate(util::id_obj_name(m_image_ctx->name), comp, &op, &m_out_bl); comp->release(); } else { send_v2_get_name(); } } template Context *OpenRequest::handle_v2_detect_header(int *result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; if (*result == -ENOENT) { send_v1_detect_header(); } else if (*result < 0) { lderr(cct) << "failed to stat v2 image header: " << cpp_strerror(*result) << dendl; send_close_image(*result); } else { m_image_ctx->old_format = false; send_v2_get_id(); } return nullptr; } template void OpenRequest::send_v2_get_id() { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << dendl; librados::ObjectReadOperation op; cls_client::get_id_start(&op); using klass = OpenRequest; librados::AioCompletion *comp = create_rados_callback(this); m_out_bl.clear(); m_image_ctx->md_ctx.aio_operate(util::id_obj_name(m_image_ctx->name), comp, &op, &m_out_bl); comp->release(); } template Context *OpenRequest::handle_v2_get_id(int *result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; if (*result == 0) { auto it = m_out_bl.cbegin(); *result = cls_client::get_id_finish(&it, &m_image_ctx->id); } if (*result < 0) { lderr(cct) << "failed to retrieve image id: " << cpp_strerror(*result) << dendl; send_close_image(*result); } else { send_v2_get_initial_metadata(); } return nullptr; } template void OpenRequest::send_v2_get_name() { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << dendl; librados::ObjectReadOperation op; cls_client::dir_get_name_start(&op, m_image_ctx->id); using klass = OpenRequest; librados::AioCompletion *comp = create_rados_callback< klass, &klass::handle_v2_get_name>(this); m_out_bl.clear(); m_image_ctx->md_ctx.aio_operate(RBD_DIRECTORY, comp, &op, &m_out_bl); comp->release(); } template Context *OpenRequest::handle_v2_get_name(int *result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; if (*result == 0) { auto it = m_out_bl.cbegin(); *result = cls_client::dir_get_name_finish(&it, &m_image_ctx->name); } if (*result < 0 && *result != -ENOENT) { lderr(cct) << "failed to retrieve name: " << cpp_strerror(*result) << dendl; send_close_image(*result); } else if (*result == -ENOENT) { // image does not exist in directory, look in the trash bin ldout(cct, 10) << "image id " << m_image_ctx->id << " does not exist in " << "rbd directory, searching in rbd trash..." << dendl; send_v2_get_name_from_trash(); } else { send_v2_get_initial_metadata(); } return nullptr; } template void OpenRequest::send_v2_get_name_from_trash() { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << dendl; librados::ObjectReadOperation op; cls_client::trash_get_start(&op, m_image_ctx->id); using klass = OpenRequest; librados::AioCompletion *comp = create_rados_callback< klass, &klass::handle_v2_get_name_from_trash>(this); m_out_bl.clear(); m_image_ctx->md_ctx.aio_operate(RBD_TRASH, comp, &op, &m_out_bl); comp->release(); } template Context *OpenRequest::handle_v2_get_name_from_trash(int *result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; cls::rbd::TrashImageSpec trash_spec; if (*result == 0) { auto it = m_out_bl.cbegin(); *result = cls_client::trash_get_finish(&it, &trash_spec); m_image_ctx->name = trash_spec.name; } if (*result < 0) { if (*result == -EOPNOTSUPP) { *result = -ENOENT; } if (*result == -ENOENT) { ldout(cct, 5) << "failed to retrieve name for image id " << m_image_ctx->id << dendl; } else { lderr(cct) << "failed to retrieve name from trash: " << cpp_strerror(*result) << dendl; } send_close_image(*result); } else { send_v2_get_initial_metadata(); } return nullptr; } template void OpenRequest::send_v2_get_initial_metadata() { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << dendl; m_image_ctx->old_format = false; m_image_ctx->header_oid = util::header_name(m_image_ctx->id); librados::ObjectReadOperation op; cls_client::get_size_start(&op, CEPH_NOSNAP); cls_client::get_object_prefix_start(&op); cls_client::get_features_start(&op, true); using klass = OpenRequest; librados::AioCompletion *comp = create_rados_callback< klass, &klass::handle_v2_get_initial_metadata>(this); m_out_bl.clear(); m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, comp, &op, &m_out_bl); comp->release(); } template Context *OpenRequest::handle_v2_get_initial_metadata(int *result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; auto it = m_out_bl.cbegin(); if (*result >= 0) { uint64_t size; *result = cls_client::get_size_finish(&it, &size, &m_image_ctx->order); } if (*result >= 0) { *result = cls_client::get_object_prefix_finish(&it, &m_image_ctx->object_prefix); } if (*result >= 0) { uint64_t incompatible_features; *result = cls_client::get_features_finish(&it, &m_image_ctx->features, &incompatible_features); } if (*result < 0) { lderr(cct) << "failed to retrieve initial metadata: " << cpp_strerror(*result) << dendl; send_close_image(*result); return nullptr; } if (m_image_ctx->test_features(RBD_FEATURE_STRIPINGV2)) { send_v2_get_stripe_unit_count(); } else { send_v2_get_create_timestamp(); } return nullptr; } template void OpenRequest::send_v2_get_stripe_unit_count() { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << dendl; librados::ObjectReadOperation op; cls_client::get_stripe_unit_count_start(&op); using klass = OpenRequest; librados::AioCompletion *comp = create_rados_callback< klass, &klass::handle_v2_get_stripe_unit_count>(this); m_out_bl.clear(); m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, comp, &op, &m_out_bl); comp->release(); } template Context *OpenRequest::handle_v2_get_stripe_unit_count(int *result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; if (*result == 0) { auto it = m_out_bl.cbegin(); *result = cls_client::get_stripe_unit_count_finish( &it, &m_image_ctx->stripe_unit, &m_image_ctx->stripe_count); } if (*result == -ENOEXEC || *result == -EINVAL) { *result = 0; } if (*result < 0) { lderr(cct) << "failed to read striping metadata: " << cpp_strerror(*result) << dendl; send_close_image(*result); return nullptr; } send_v2_get_create_timestamp(); return nullptr; } template void OpenRequest::send_v2_get_create_timestamp() { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << dendl; librados::ObjectReadOperation op; cls_client::get_create_timestamp_start(&op); using klass = OpenRequest; librados::AioCompletion *comp = create_rados_callback< klass, &klass::handle_v2_get_create_timestamp>(this); m_out_bl.clear(); m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, comp, &op, &m_out_bl); comp->release(); } template Context *OpenRequest::handle_v2_get_create_timestamp(int *result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; if (*result == 0) { auto it = m_out_bl.cbegin(); *result = cls_client::get_create_timestamp_finish(&it, &m_image_ctx->create_timestamp); } if (*result < 0 && *result != -EOPNOTSUPP) { lderr(cct) << "failed to retrieve create_timestamp: " << cpp_strerror(*result) << dendl; send_close_image(*result); return nullptr; } send_v2_get_access_modify_timestamp(); return nullptr; } template void OpenRequest::send_v2_get_access_modify_timestamp() { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << dendl; librados::ObjectReadOperation op; cls_client::get_access_timestamp_start(&op); cls_client::get_modify_timestamp_start(&op); //TODO: merge w/ create timestamp query after luminous EOLed using klass = OpenRequest; librados::AioCompletion *comp = create_rados_callback< klass, &klass::handle_v2_get_access_modify_timestamp>(this); m_out_bl.clear(); m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, comp, &op, &m_out_bl); comp->release(); } template Context *OpenRequest::handle_v2_get_access_modify_timestamp(int *result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; if (*result == 0) { auto it = m_out_bl.cbegin(); *result = cls_client::get_access_timestamp_finish(&it, &m_image_ctx->access_timestamp); if (*result == 0) *result = cls_client::get_modify_timestamp_finish(&it, &m_image_ctx->modify_timestamp); } if (*result < 0 && *result != -EOPNOTSUPP) { lderr(cct) << "failed to retrieve access/modify_timestamp: " << cpp_strerror(*result) << dendl; send_close_image(*result); return nullptr; } send_v2_get_data_pool(); return nullptr; } template void OpenRequest::send_v2_get_data_pool() { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << dendl; librados::ObjectReadOperation op; cls_client::get_data_pool_start(&op); using klass = OpenRequest; librados::AioCompletion *comp = create_rados_callback< klass, &klass::handle_v2_get_data_pool>(this); m_out_bl.clear(); m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, comp, &op, &m_out_bl); comp->release(); } template Context *OpenRequest::handle_v2_get_data_pool(int *result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; int64_t data_pool_id = -1; if (*result == 0) { auto it = m_out_bl.cbegin(); *result = cls_client::get_data_pool_finish(&it, &data_pool_id); } else if (*result == -EOPNOTSUPP) { *result = 0; } if (*result < 0) { lderr(cct) << "failed to read data pool: " << cpp_strerror(*result) << dendl; send_close_image(*result); return nullptr; } if (data_pool_id != -1) { *result = util::create_ioctx(m_image_ctx->md_ctx, "data pool", data_pool_id, {}, &m_image_ctx->data_ctx); if (*result < 0) { if (*result != -ENOENT) { send_close_image(*result); return nullptr; } m_image_ctx->data_ctx.close(); } else { m_image_ctx->rebuild_data_io_context(); } } else { data_pool_id = m_image_ctx->md_ctx.get_id(); } m_image_ctx->init_layout(data_pool_id); send_refresh(); return nullptr; } template void OpenRequest::send_refresh() { m_image_ctx->init(); CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << dendl; m_image_ctx->config_watcher = ConfigWatcher::create(*m_image_ctx); m_image_ctx->config_watcher->init(); using klass = OpenRequest; RefreshRequest *req = RefreshRequest::create( *m_image_ctx, false, m_skip_open_parent_image, create_context_callback(this)); req->send(); } template Context *OpenRequest::handle_refresh(int *result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to refresh image: " << cpp_strerror(*result) << dendl; send_close_image(*result); return nullptr; } send_init_plugin_registry(); return nullptr; } template void OpenRequest::send_init_plugin_registry() { CephContext *cct = m_image_ctx->cct; auto plugins = m_image_ctx->config.template get_val( "rbd_plugins"); ldout(cct, 10) << __func__ << ": plugins=" << plugins << dendl; auto ctx = create_context_callback< OpenRequest, &OpenRequest::handle_init_plugin_registry>(this); m_image_ctx->plugin_registry->init(plugins, ctx); } template Context* OpenRequest::handle_init_plugin_registry(int *result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to initialize plugin registry: " << cpp_strerror(*result) << dendl; send_close_image(*result); return nullptr; } return send_init_cache(result); } template Context *OpenRequest::send_init_cache(int *result) { if (!m_image_ctx->cache || m_image_ctx->child != nullptr || !m_image_ctx->data_ctx.is_valid()) { return send_register_watch(result); } CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << dendl; size_t max_dirty = m_image_ctx->config.template get_val( "rbd_cache_max_dirty"); auto writethrough_until_flush = m_image_ctx->config.template get_val( "rbd_cache_writethrough_until_flush"); auto cache_policy = m_image_ctx->config.template get_val( "rbd_cache_policy"); if (cache_policy == "writearound") { auto cache = cache::WriteAroundObjectDispatch::create( m_image_ctx, max_dirty, writethrough_until_flush); cache->init(); m_image_ctx->readahead.set_max_readahead_size(0); } else if (cache_policy == "writethrough" || cache_policy == "writeback") { if (cache_policy == "writethrough") { max_dirty = 0; } auto cache = cache::ObjectCacherObjectDispatch::create( m_image_ctx, max_dirty, writethrough_until_flush); cache->init(); // readahead requires the object cacher cache m_image_ctx->readahead.set_trigger_requests( m_image_ctx->config.template get_val("rbd_readahead_trigger_requests")); m_image_ctx->readahead.set_max_readahead_size( m_image_ctx->config.template get_val("rbd_readahead_max_bytes")); } return send_register_watch(result); } template Context *OpenRequest::send_register_watch(int *result) { if ((m_image_ctx->read_only_flags & IMAGE_READ_ONLY_FLAG_USER) != 0U) { return send_set_snap(result); } CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << dendl; using klass = OpenRequest; Context *ctx = create_context_callback< klass, &klass::handle_register_watch>(this); m_image_ctx->register_watch(ctx); return nullptr; } template Context *OpenRequest::handle_register_watch(int *result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; if (*result == -EPERM) { ldout(cct, 5) << "user does not have write permission" << dendl; send_close_image(*result); return nullptr; } else if (*result < 0) { lderr(cct) << "failed to register watch: " << cpp_strerror(*result) << dendl; send_close_image(*result); return nullptr; } return send_set_snap(result); } template Context *OpenRequest::send_set_snap(int *result) { if (m_image_ctx->snap_name.empty() && m_image_ctx->open_snap_id == CEPH_NOSNAP) { *result = 0; return finalize(*result); } CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << dendl; uint64_t snap_id = CEPH_NOSNAP; std::swap(m_image_ctx->open_snap_id, snap_id); if (snap_id == CEPH_NOSNAP) { std::shared_lock image_locker{m_image_ctx->image_lock}; snap_id = m_image_ctx->get_snap_id(m_image_ctx->snap_namespace, m_image_ctx->snap_name); } if (snap_id == CEPH_NOSNAP) { lderr(cct) << "failed to find snapshot " << m_image_ctx->snap_name << dendl; send_close_image(-ENOENT); return nullptr; } using klass = OpenRequest; SetSnapRequest *req = SetSnapRequest::create( *m_image_ctx, snap_id, create_context_callback(this)); req->send(); return nullptr; } template Context *OpenRequest::handle_set_snap(int *result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to set image snapshot: " << cpp_strerror(*result) << dendl; send_close_image(*result); return nullptr; } return finalize(*result); } template Context *OpenRequest::finalize(int r) { if (r == 0) { auto io_scheduler_cfg = m_image_ctx->config.template get_val("rbd_io_scheduler"); if (io_scheduler_cfg == "simple" && !m_image_ctx->read_only) { auto io_scheduler = io::SimpleSchedulerObjectDispatch::create(m_image_ctx); io_scheduler->init(); } } return m_on_finish; } template void OpenRequest::send_close_image(int error_result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << this << " " << __func__ << dendl; m_error_result = error_result; using klass = OpenRequest; Context *ctx = create_context_callback( this); CloseRequest *req = CloseRequest::create(m_image_ctx, ctx); req->send(); } template Context *OpenRequest::handle_close_image(int *result) { CephContext *cct = m_image_ctx->cct; ldout(cct, 10) << __func__ << ": r=" << *result << dendl; if (*result < 0) { lderr(cct) << "failed to close image: " << cpp_strerror(*result) << dendl; } if (m_error_result < 0) { *result = m_error_result; } return m_on_finish; } } // namespace image } // namespace librbd template class librbd::image::OpenRequest;