]> git.proxmox.com Git - ceph.git/blob - ceph/src/tools/rbd/action/Create.cc
02d5621177e644a6a0b46e4839d86e94b3b75eb9
[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/ceph_mutex.h"
8 #include "common/config_proxy.h"
9 #include "common/errno.h"
10 #include "global/global_context.h"
11 #include <iostream>
12 #include <boost/program_options.hpp>
13
14 namespace rbd {
15 namespace action {
16 namespace create {
17
18 namespace at = argument_types;
19 namespace po = boost::program_options;
20
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);
25 }
26
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);
35 }
36
37 void thick_provision_writer_completion(rbd_completion_t, void *);
38
39 struct thick_provision_writer {
40 librbd::Image *image;
41 ceph::mutex lock = ceph::make_mutex("thick_provision_writer::lock");
42 ceph::condition_variable cond;
43 bufferlist bl;
44 uint64_t chunk_size;
45 const int block_size;
46 uint64_t concurr;
47 struct {
48 uint64_t in_flight;
49 int io_error;
50 } io_status;
51
52 // Constructor
53 explicit thick_provision_writer(librbd::Image *i, librbd::ImageOptions &o)
54 : image(i),
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 std::lock_guard 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 std::lock_guard l{lock};
98 io_status.io_error = r;
99 }
100 return r;
101 }
102
103 int wait_for(uint64_t max) {
104 std::unique_lock l{lock};
105 int r = io_status.io_error;
106
107 while (io_status.in_flight > max) {
108 cond.wait_for(l, 200ms);
109 }
110 return r;
111 }
112 };
113
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);
117
118 int r = ac->get_return_value();
119 tc->lock.lock();
120 if (r < 0 && tc->io_status.io_error >= 0) {
121 tc->io_status.io_error = r;
122 }
123 tc->io_status.in_flight--;
124 tc->cond.notify_all();
125 tc->lock.unlock();
126 ac->release();
127 }
128
129 int write_data(librbd::Image &image, librbd::ImageOptions &opts,
130 bool no_progress) {
131 uint64_t image_size;
132 int r = 0;
133 utils::ProgressContext pc("Thick provisioning", no_progress);
134
135 if (image.size(&image_size) != 0) {
136 return -EINVAL;
137 }
138
139 thick_provision_writer tpw(&image, opts);
140 uint64_t off;
141 uint64_t i;
142 for (off = 0; off < image_size;) {
143 i = 0;
144 while (i < tpw.concurr && off < image_size) {
145 tpw.wait_for(tpw.concurr - 1);
146 r = tpw.start_io(off);
147 if (r != 0) {
148 goto err_writesame;
149 }
150 ++i;
151 off += tpw.chunk_size;
152 if(off > image_size) {
153 off = image_size;
154 }
155 pc.update_progress(off, image_size);
156 }
157 }
158
159 tpw.wait_for(0);
160 r = image.flush();
161 if (r < 0) {
162 std::cerr << "rbd: failed to flush at the end: " << cpp_strerror(r)
163 << std::endl;
164 goto err_writesame;
165 }
166 pc.finish();
167
168 return r;
169
170 err_writesame:
171 tpw.wait_for(0);
172 pc.fail();
173
174 return r;
175 }
176
177 int thick_write(const std::string &image_name,librados::IoCtx &io_ctx,
178 librbd::ImageOptions &opts, bool no_progress) {
179 int r = 0;
180 librbd::Image image;
181
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");
186 ceph_assert(r == 0);
187 r = utils::open_image(io_ctx, image_name, false, &image);
188 if (r < 0) {
189 return r;
190 }
191
192 r = write_data(image, opts, no_progress);
193
194 image.close();
195
196 return r;
197 }
198
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);
210 if (r < 0) {
211 return r;
212 }
213
214 librbd::ImageOptions opts;
215 r = utils::get_image_options(vm, true, &opts);
216 if (r < 0) {
217 return r;
218 }
219
220 uint64_t size;
221 r = utils::get_image_size(vm, &size);
222 if (r < 0) {
223 return r;
224 }
225
226 librados::Rados rados;
227 librados::IoCtx io_ctx;
228 r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
229 if (r < 0) {
230 return r;
231 }
232
233 librbd::RBD rbd;
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."
238 << std::endl;
239 return r;
240 } else if (r < 0) {
241 std::cerr << "rbd: create error: " << cpp_strerror(r) << std::endl;
242 return r;
243 }
244
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>());
247 if (r < 0) {
248 std::cerr << "rbd: image created but error encountered during thick provisioning: "
249 << cpp_strerror(r) << std::endl;
250 return r;
251 }
252 }
253 return 0;
254 }
255
256 Shell::Action action(
257 {"create"}, {}, "Create an empty image.", at::get_long_features_help(),
258 &get_arguments, &execute);
259
260 } // namespace create
261 } // namespace action
262 } // namespace rbd