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/errno.h"
9 #include <boost/program_options.hpp>
10 #include "common/Cond.h"
11 #include "common/Mutex.h"
17 namespace at
= argument_types
;
18 namespace po
= boost::program_options
;
20 static int do_create(librbd::RBD
&rbd
, librados::IoCtx
& io_ctx
,
21 const char *imgname
, uint64_t size
,
22 librbd::ImageOptions
& opts
) {
23 return rbd
.create4(io_ctx
, imgname
, size
, opts
);
26 void get_arguments(po::options_description
*positional
,
27 po::options_description
*options
) {
28 at::add_image_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
29 at::add_create_image_options(options
, true);
30 options
->add_options()
31 (at::IMAGE_THICK_PROVISION
.c_str(), po::bool_switch(), "fully allocate storage and zero image");
32 at::add_size_option(options
);
33 at::add_no_progress_option(options
);
36 void thick_provision_writer_completion(rbd_completion_t
, void *);
38 struct thick_provision_writer
{
52 explicit thick_provision_writer(librbd::Image
*i
, librbd::ImageOptions
&o
)
54 lock("thick_provision_writer::lock"),
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 Mutex::Locker
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 Mutex::Locker
l(lock
);
98 io_status
.io_error
= r
;
103 int wait_for(uint64_t max
) {
104 Mutex::Locker
l(lock
);
105 int r
= io_status
.io_error
;
107 while (io_status
.in_flight
> max
) {
109 dur
.set_from_double(.2);
110 cond
.WaitInterval(lock
, dur
);
116 void thick_provision_writer_completion(rbd_completion_t rc
, void *pc
) {
117 librbd::RBD::AioCompletion
*ac
= (librbd::RBD::AioCompletion
*)rc
;
118 thick_provision_writer
*tc
= static_cast<thick_provision_writer
*>(pc
);
120 int r
= ac
->get_return_value();
122 if (r
< 0 && tc
->io_status
.io_error
>= 0) {
123 tc
->io_status
.io_error
= r
;
125 tc
->io_status
.in_flight
--;
131 int write_data(librbd::Image
&image
, librbd::ImageOptions
&opts
,
135 utils::ProgressContext
pc("Thick provisioning", no_progress
);
137 if (image
.size(&image_size
) != 0) {
141 thick_provision_writer
tpw(&image
, opts
);
144 for (off
= 0; off
< image_size
;) {
146 while (i
< tpw
.concurr
&& off
< image_size
) {
147 tpw
.wait_for(tpw
.concurr
- 1);
148 r
= tpw
.start_io(off
);
153 off
+= tpw
.chunk_size
;
154 if(off
> image_size
) {
157 pc
.update_progress(off
, image_size
);
164 std::cerr
<< "rbd: failed to flush at the end: " << cpp_strerror(r
)
179 int thick_write(const std::string
&image_name
,librados::IoCtx
&io_ctx
,
180 librbd::ImageOptions
&opts
, bool no_progress
) {
184 // To prevent writesame from discarding data, thick_write sets
185 // the rbd_discard_on_zeroed_write_same option to false.
186 ceph_assert(g_ceph_context
!= nullptr);
187 r
= g_conf().set_val("rbd_discard_on_zeroed_write_same", "false");
189 r
= utils::open_image(io_ctx
, image_name
, false, &image
);
194 r
= write_data(image
, opts
, no_progress
);
201 int execute(const po::variables_map
&vm
,
202 const std::vector
<std::string
> &ceph_global_init_args
) {
203 size_t arg_index
= 0;
204 std::string pool_name
;
205 std::string namespace_name
;
206 std::string image_name
;
207 std::string snap_name
;
208 int r
= utils::get_pool_image_snapshot_names(
209 vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
, &pool_name
, &namespace_name
,
210 &image_name
, &snap_name
, true, utils::SNAPSHOT_PRESENCE_NONE
,
211 utils::SPEC_VALIDATION_FULL
);
216 librbd::ImageOptions opts
;
217 r
= utils::get_image_options(vm
, true, &opts
);
223 r
= utils::get_image_size(vm
, &size
);
228 librados::Rados rados
;
229 librados::IoCtx io_ctx
;
230 r
= utils::init(pool_name
, namespace_name
, &rados
, &io_ctx
);
236 r
= do_create(rbd
, io_ctx
, image_name
.c_str(), size
, opts
);
237 if (!namespace_name
.empty() && r
== -ENOENT
) {
238 std::cerr
<< "rbd: namespace not found - it must be created with "
239 << "'rbd namespace create' before creating an image."
243 std::cerr
<< "rbd: create error: " << cpp_strerror(r
) << std::endl
;
247 if (vm
.count(at::IMAGE_THICK_PROVISION
) && vm
[at::IMAGE_THICK_PROVISION
].as
<bool>()) {
248 r
= thick_write(image_name
, io_ctx
, opts
, vm
[at::NO_PROGRESS
].as
<bool>());
250 std::cerr
<< "rbd: image created but error encountered during thick provisioning: "
251 << cpp_strerror(r
) << std::endl
;
258 Shell::Action
action(
259 {"create"}, {}, "Create an empty image.", at::get_long_features_help(),
260 &get_arguments
, &execute
);
262 } // namespace create
263 } // namespace action