// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "tools/rbd/ArgumentTypes.h" #include "tools/rbd/Shell.h" #include "tools/rbd/Utils.h" #include "common/ceph_mutex.h" #include "common/config_proxy.h" #include "common/errno.h" #include "global/global_context.h" #include #include namespace rbd { namespace action { namespace create { namespace at = argument_types; namespace po = boost::program_options; static int do_create(librbd::RBD &rbd, librados::IoCtx& io_ctx, const char *imgname, uint64_t size, librbd::ImageOptions& opts) { return rbd.create4(io_ctx, imgname, size, opts); } void get_arguments(po::options_description *positional, po::options_description *options) { at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); at::add_create_image_options(options, true); options->add_options() (at::IMAGE_THICK_PROVISION.c_str(), po::bool_switch(), "fully allocate storage and zero image"); at::add_size_option(options); at::add_no_progress_option(options); } void thick_provision_writer_completion(rbd_completion_t, void *); struct thick_provision_writer { librbd::Image *image; ceph::mutex lock = ceph::make_mutex("thick_provision_writer::lock"); ceph::condition_variable cond; uint64_t chunk_size; uint64_t concurr; struct { uint64_t in_flight; int io_error; } io_status; // Constructor explicit thick_provision_writer(librbd::Image *i, librbd::ImageOptions &o) : image(i) { // If error cases occur, the code is aborted, because // constructor cannot return error value. ceph_assert(g_ceph_context != nullptr); librbd::image_info_t info; int r = image->stat(info, sizeof(info)); ceph_assert(r >= 0); uint64_t order = info.order; if (order == 0) { order = g_conf().get_val("rbd_default_order"); } auto stripe_count = std::max(1U, image->get_stripe_count()); chunk_size = (1ull << order) * stripe_count; concurr = std::max( 1U, g_conf().get_val("rbd_concurrent_management_ops") / stripe_count); io_status.in_flight = 0; io_status.io_error = 0; } int start_io(uint64_t write_offset) { { std::lock_guard l{lock}; io_status.in_flight++; if (io_status.in_flight > concurr) { io_status.in_flight--; return -EINVAL; } } librbd::RBD::AioCompletion *c; c = new librbd::RBD::AioCompletion(this, thick_provision_writer_completion); int r; r = image->aio_write_zeroes(write_offset, chunk_size, c, RBD_WRITE_ZEROES_FLAG_THICK_PROVISION, LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL); if (r < 0) { std::lock_guard l{lock}; io_status.io_error = r; } return r; } int wait_for(uint64_t max) { using namespace std::chrono_literals; std::unique_lock l{lock}; int r = io_status.io_error; while (io_status.in_flight > max) { cond.wait_for(l, 200ms); } return r; } }; void thick_provision_writer_completion(rbd_completion_t rc, void *pc) { librbd::RBD::AioCompletion *ac = (librbd::RBD::AioCompletion *)rc; thick_provision_writer *tc = static_cast(pc); int r = ac->get_return_value(); tc->lock.lock(); if (r < 0 && tc->io_status.io_error >= 0) { tc->io_status.io_error = r; } tc->io_status.in_flight--; tc->cond.notify_all(); tc->lock.unlock(); ac->release(); } int write_data(librbd::Image &image, librbd::ImageOptions &opts, bool no_progress) { uint64_t image_size; int r = 0; utils::ProgressContext pc("Thick provisioning", no_progress); if (image.size(&image_size) != 0) { return -EINVAL; } thick_provision_writer tpw(&image, opts); uint64_t off; uint64_t i; for (off = 0; off < image_size;) { i = 0; while (i < tpw.concurr && off < image_size) { tpw.wait_for(tpw.concurr - 1); r = tpw.start_io(off); if (r != 0) { goto err_writesame; } ++i; off += tpw.chunk_size; if(off > image_size) { off = image_size; } pc.update_progress(off, image_size); } } tpw.wait_for(0); r = image.flush(); if (r < 0) { std::cerr << "rbd: failed to flush at the end: " << cpp_strerror(r) << std::endl; goto err_writesame; } pc.finish(); return r; err_writesame: tpw.wait_for(0); pc.fail(); return r; } int thick_write(const std::string &image_name,librados::IoCtx &io_ctx, librbd::ImageOptions &opts, bool no_progress) { int r; librbd::Image image; r = utils::open_image(io_ctx, image_name, false, &image); if (r < 0) { return r; } r = write_data(image, opts, no_progress); image.close(); return r; } int execute(const po::variables_map &vm, const std::vector &ceph_global_init_args) { size_t arg_index = 0; std::string pool_name; std::string namespace_name; std::string image_name; std::string snap_name; int r = utils::get_pool_image_snapshot_names( vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_FULL); if (r < 0) { return r; } librbd::ImageOptions opts; r = utils::get_image_options(vm, true, &opts); if (r < 0) { return r; } uint64_t size; r = utils::get_image_size(vm, &size); if (r < 0) { return r; } librados::Rados rados; librados::IoCtx io_ctx; r = utils::init(pool_name, namespace_name, &rados, &io_ctx); if (r < 0) { return r; } librbd::RBD rbd; r = do_create(rbd, io_ctx, image_name.c_str(), size, opts); if (!namespace_name.empty() && r == -ENOENT) { std::cerr << "rbd: namespace not found - it must be created with " << "'rbd namespace create' before creating an image." << std::endl; return r; } else if (r < 0) { std::cerr << "rbd: create error: " << cpp_strerror(r) << std::endl; return r; } if (vm.count(at::IMAGE_THICK_PROVISION) && vm[at::IMAGE_THICK_PROVISION].as()) { r = thick_write(image_name, io_ctx, opts, vm[at::NO_PROGRESS].as()); if (r < 0) { std::cerr << "rbd: image created but error encountered during thick provisioning: " << cpp_strerror(r) << std::endl; return r; } } return 0; } Shell::Action action( {"create"}, {}, "Create an empty image.", at::get_long_features_help(), &get_arguments, &execute); } // namespace create } // namespace action } // namespace rbd