]> git.proxmox.com Git - ceph.git/blob - ceph/src/tools/rbd/action/Create.cc
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / tools / rbd / action / Create.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "tools/rbd/ArgumentTypes.h"
5 #include "tools/rbd/Shell.h"
6 #include "tools/rbd/Utils.h"
7 #include "common/errno.h"
8 #include <iostream>
9 #include <boost/program_options.hpp>
10 #include "common/Cond.h"
11 #include "common/Mutex.h"
12
13 namespace rbd {
14 namespace action {
15 namespace create {
16
17 namespace at = argument_types;
18 namespace po = boost::program_options;
19
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);
24 }
25
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);
34 }
35
36 void thick_provision_writer_completion(rbd_completion_t, void *);
37
38 struct thick_provision_writer {
39 librbd::Image *image;
40 Mutex lock;
41 Cond cond;
42 bufferlist bl;
43 uint64_t chunk_size;
44 const int block_size;
45 uint64_t concurr;
46 struct {
47 uint64_t in_flight;
48 int io_error;
49 } io_status;
50
51 // Constructor
52 explicit thick_provision_writer(librbd::Image *i, librbd::ImageOptions &o)
53 : image(i),
54 lock("thick_provision_writer::lock"),
55 block_size(512) // 512 Bytes
56 {
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);
61
62 librbd::image_info_t info;
63 int r = image->stat(info, sizeof(info));
64 ceph_assert(r >= 0);
65 uint64_t order;
66 if (info.order == 0) {
67 order = g_conf().get_val<uint64_t>("rbd_default_order");
68 } else {
69 order = info.order;
70 }
71 chunk_size = (1ull << order);
72 if (image->get_stripe_unit() < chunk_size) {
73 chunk_size = image->get_stripe_unit();
74 }
75
76 concurr = g_conf().get_val<uint64_t>("rbd_concurrent_management_ops");
77 io_status.in_flight = 0;
78 io_status.io_error = 0;
79 }
80
81 int start_io(uint64_t write_offset)
82 {
83 {
84 Mutex::Locker l(lock);
85 io_status.in_flight++;
86 if (io_status.in_flight > concurr) {
87 io_status.in_flight--;
88 return -EINVAL;
89 }
90 }
91
92 librbd::RBD::AioCompletion *c;
93 c = new librbd::RBD::AioCompletion(this, thick_provision_writer_completion);
94 int r;
95 r = image->aio_writesame(write_offset, chunk_size, bl, c, LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL);
96 if (r < 0) {
97 Mutex::Locker l(lock);
98 io_status.io_error = r;
99 }
100 return r;
101 }
102
103 int wait_for(uint64_t max) {
104 Mutex::Locker l(lock);
105 int r = io_status.io_error;
106
107 while (io_status.in_flight > max) {
108 utime_t dur;
109 dur.set_from_double(.2);
110 cond.WaitInterval(lock, dur);
111 }
112 return r;
113 }
114 };
115
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);
119
120 int r = ac->get_return_value();
121 tc->lock.Lock();
122 if (r < 0 && tc->io_status.io_error >= 0) {
123 tc->io_status.io_error = r;
124 }
125 tc->io_status.in_flight--;
126 tc->cond.Signal();
127 tc->lock.Unlock();
128 ac->release();
129 }
130
131 int write_data(librbd::Image &image, librbd::ImageOptions &opts,
132 bool no_progress) {
133 uint64_t image_size;
134 int r = 0;
135 utils::ProgressContext pc("Thick provisioning", no_progress);
136
137 if (image.size(&image_size) != 0) {
138 return -EINVAL;
139 }
140
141 thick_provision_writer tpw(&image, opts);
142 uint64_t off;
143 uint64_t i;
144 for (off = 0; off < image_size;) {
145 i = 0;
146 while (i < tpw.concurr && off < image_size) {
147 tpw.wait_for(tpw.concurr - 1);
148 r = tpw.start_io(off);
149 if (r != 0) {
150 goto err_writesame;
151 }
152 ++i;
153 off += tpw.chunk_size;
154 if(off > image_size) {
155 off = image_size;
156 }
157 pc.update_progress(off, image_size);
158 }
159 }
160
161 tpw.wait_for(0);
162 r = image.flush();
163 if (r < 0) {
164 std::cerr << "rbd: failed to flush at the end: " << cpp_strerror(r)
165 << std::endl;
166 goto err_writesame;
167 }
168 pc.finish();
169
170 return r;
171
172 err_writesame:
173 tpw.wait_for(0);
174 pc.fail();
175
176 return r;
177 }
178
179 int thick_write(const std::string &image_name,librados::IoCtx &io_ctx,
180 librbd::ImageOptions &opts, bool no_progress) {
181 int r = 0;
182 librbd::Image image;
183
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");
188 ceph_assert(r == 0);
189 r = utils::open_image(io_ctx, image_name, false, &image);
190 if (r < 0) {
191 return r;
192 }
193
194 r = write_data(image, opts, no_progress);
195
196 image.close();
197
198 return r;
199 }
200
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);
212 if (r < 0) {
213 return r;
214 }
215
216 librbd::ImageOptions opts;
217 r = utils::get_image_options(vm, true, &opts);
218 if (r < 0) {
219 return r;
220 }
221
222 uint64_t size;
223 r = utils::get_image_size(vm, &size);
224 if (r < 0) {
225 return r;
226 }
227
228 librados::Rados rados;
229 librados::IoCtx io_ctx;
230 r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
231 if (r < 0) {
232 return r;
233 }
234
235 librbd::RBD rbd;
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."
240 << std::endl;
241 return r;
242 } else if (r < 0) {
243 std::cerr << "rbd: create error: " << cpp_strerror(r) << std::endl;
244 return r;
245 }
246
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>());
249 if (r < 0) {
250 std::cerr << "rbd: image created but error encountered during thick provisioning: "
251 << cpp_strerror(r) << std::endl;
252 return r;
253 }
254 }
255 return 0;
256 }
257
258 Shell::Action action(
259 {"create"}, {}, "Create an empty image.", at::get_long_features_help(),
260 &get_arguments, &execute);
261
262 } // namespace create
263 } // namespace action
264 } // namespace rbd