1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "tools/rbd/ArgumentTypes.h"
5 #include "tools/rbd/Shell.h"
6 #include "tools/rbd/Utils.h"
7 #include "common/ceph_mutex.h"
8 #include "common/config_proxy.h"
9 #include "common/errno.h"
10 #include "global/global_context.h"
12 #include <boost/program_options.hpp>
18 namespace at
= argument_types
;
19 namespace po
= boost::program_options
;
21 static int do_create(librbd::RBD
&rbd
, librados::IoCtx
& io_ctx
,
22 const char *imgname
, uint64_t size
,
23 librbd::ImageOptions
& opts
) {
24 return rbd
.create4(io_ctx
, imgname
, size
, opts
);
27 void get_arguments(po::options_description
*positional
,
28 po::options_description
*options
) {
29 at::add_image_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
30 at::add_create_image_options(options
, true);
31 options
->add_options()
32 (at::IMAGE_THICK_PROVISION
.c_str(), po::bool_switch(), "fully allocate storage and zero image");
33 at::add_size_option(options
);
34 at::add_no_progress_option(options
);
37 void thick_provision_writer_completion(rbd_completion_t
, void *);
39 struct thick_provision_writer
{
41 ceph::mutex lock
= ceph::make_mutex("thick_provision_writer::lock");
42 ceph::condition_variable cond
;
53 explicit thick_provision_writer(librbd::Image
*i
, librbd::ImageOptions
&o
)
55 block_size(512) // 512 Bytes
57 // If error cases occur, the code is aborted, because
58 // constructor cannot return error value.
59 ceph_assert(g_ceph_context
!= nullptr);
60 bl
.append_zero(block_size
);
62 librbd::image_info_t info
;
63 int r
= image
->stat(info
, sizeof(info
));
66 if (info
.order
== 0) {
67 order
= g_conf().get_val
<uint64_t>("rbd_default_order");
71 chunk_size
= (1ull << order
);
72 if (image
->get_stripe_unit() < chunk_size
) {
73 chunk_size
= image
->get_stripe_unit();
76 concurr
= g_conf().get_val
<uint64_t>("rbd_concurrent_management_ops");
77 io_status
.in_flight
= 0;
78 io_status
.io_error
= 0;
81 int start_io(uint64_t write_offset
)
84 std::lock_guard l
{lock
};
85 io_status
.in_flight
++;
86 if (io_status
.in_flight
> concurr
) {
87 io_status
.in_flight
--;
92 librbd::RBD::AioCompletion
*c
;
93 c
= new librbd::RBD::AioCompletion(this, thick_provision_writer_completion
);
95 r
= image
->aio_writesame(write_offset
, chunk_size
, bl
, c
, LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL
);
97 std::lock_guard l
{lock
};
98 io_status
.io_error
= r
;
103 int wait_for(uint64_t max
) {
104 std::unique_lock l
{lock
};
105 int r
= io_status
.io_error
;
107 while (io_status
.in_flight
> max
) {
108 cond
.wait_for(l
, 200ms
);
114 void thick_provision_writer_completion(rbd_completion_t rc
, void *pc
) {
115 librbd::RBD::AioCompletion
*ac
= (librbd::RBD::AioCompletion
*)rc
;
116 thick_provision_writer
*tc
= static_cast<thick_provision_writer
*>(pc
);
118 int r
= ac
->get_return_value();
120 if (r
< 0 && tc
->io_status
.io_error
>= 0) {
121 tc
->io_status
.io_error
= r
;
123 tc
->io_status
.in_flight
--;
124 tc
->cond
.notify_all();
129 int write_data(librbd::Image
&image
, librbd::ImageOptions
&opts
,
133 utils::ProgressContext
pc("Thick provisioning", no_progress
);
135 if (image
.size(&image_size
) != 0) {
139 thick_provision_writer
tpw(&image
, opts
);
142 for (off
= 0; off
< image_size
;) {
144 while (i
< tpw
.concurr
&& off
< image_size
) {
145 tpw
.wait_for(tpw
.concurr
- 1);
146 r
= tpw
.start_io(off
);
151 off
+= tpw
.chunk_size
;
152 if(off
> image_size
) {
155 pc
.update_progress(off
, image_size
);
162 std::cerr
<< "rbd: failed to flush at the end: " << cpp_strerror(r
)
177 int thick_write(const std::string
&image_name
,librados::IoCtx
&io_ctx
,
178 librbd::ImageOptions
&opts
, bool no_progress
) {
182 // To prevent writesame from discarding data, thick_write sets
183 // the rbd_discard_on_zeroed_write_same option to false.
184 ceph_assert(g_ceph_context
!= nullptr);
185 r
= g_conf().set_val("rbd_discard_on_zeroed_write_same", "false");
187 r
= utils::open_image(io_ctx
, image_name
, false, &image
);
192 r
= write_data(image
, opts
, no_progress
);
199 int execute(const po::variables_map
&vm
,
200 const std::vector
<std::string
> &ceph_global_init_args
) {
201 size_t arg_index
= 0;
202 std::string pool_name
;
203 std::string namespace_name
;
204 std::string image_name
;
205 std::string snap_name
;
206 int r
= utils::get_pool_image_snapshot_names(
207 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &namespace_name
,
208 &image_name
, &snap_name
, true, utils::SNAPSHOT_PRESENCE_NONE
,
209 utils::SPEC_VALIDATION_FULL
);
214 librbd::ImageOptions opts
;
215 r
= utils::get_image_options(vm
, true, &opts
);
221 r
= utils::get_image_size(vm
, &size
);
226 librados::Rados rados
;
227 librados::IoCtx io_ctx
;
228 r
= utils::init(pool_name
, namespace_name
, &rados
, &io_ctx
);
234 r
= do_create(rbd
, io_ctx
, image_name
.c_str(), size
, opts
);
235 if (!namespace_name
.empty() && r
== -ENOENT
) {
236 std::cerr
<< "rbd: namespace not found - it must be created with "
237 << "'rbd namespace create' before creating an image."
241 std::cerr
<< "rbd: create error: " << cpp_strerror(r
) << std::endl
;
245 if (vm
.count(at::IMAGE_THICK_PROVISION
) && vm
[at::IMAGE_THICK_PROVISION
].as
<bool>()) {
246 r
= thick_write(image_name
, io_ctx
, opts
, vm
[at::NO_PROGRESS
].as
<bool>());
248 std::cerr
<< "rbd: image created but error encountered during thick provisioning: "
249 << cpp_strerror(r
) << std::endl
;
256 Shell::Action
action(
257 {"create"}, {}, "Create an empty image.", at::get_long_features_help(),
258 &get_arguments
, &execute
);
260 } // namespace create
261 } // namespace action