]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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" | |
9f95a23c TL |
7 | #include "common/ceph_mutex.h" |
8 | #include "common/config_proxy.h" | |
7c673cae | 9 | #include "common/errno.h" |
9f95a23c | 10 | #include "global/global_context.h" |
7c673cae FG |
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); | |
11fdf7f2 TL |
31 | options->add_options() |
32 | (at::IMAGE_THICK_PROVISION.c_str(), po::bool_switch(), "fully allocate storage and zero image"); | |
7c673cae | 33 | at::add_size_option(options); |
11fdf7f2 | 34 | at::add_no_progress_option(options); |
7c673cae FG |
35 | } |
36 | ||
11fdf7f2 TL |
37 | void thick_provision_writer_completion(rbd_completion_t, void *); |
38 | ||
39 | struct thick_provision_writer { | |
40 | librbd::Image *image; | |
9f95a23c TL |
41 | ceph::mutex lock = ceph::make_mutex("thick_provision_writer::lock"); |
42 | ceph::condition_variable cond; | |
11fdf7f2 | 43 | uint64_t chunk_size; |
11fdf7f2 TL |
44 | uint64_t concurr; |
45 | struct { | |
46 | uint64_t in_flight; | |
47 | int io_error; | |
48 | } io_status; | |
49 | ||
50 | // Constructor | |
51 | explicit thick_provision_writer(librbd::Image *i, librbd::ImageOptions &o) | |
f67539c2 | 52 | : image(i) |
11fdf7f2 TL |
53 | { |
54 | // If error cases occur, the code is aborted, because | |
55 | // constructor cannot return error value. | |
56 | ceph_assert(g_ceph_context != nullptr); | |
11fdf7f2 TL |
57 | |
58 | librbd::image_info_t info; | |
59 | int r = image->stat(info, sizeof(info)); | |
60 | ceph_assert(r >= 0); | |
f67539c2 TL |
61 | |
62 | uint64_t order = info.order; | |
63 | if (order == 0) { | |
11fdf7f2 | 64 | order = g_conf().get_val<uint64_t>("rbd_default_order"); |
11fdf7f2 TL |
65 | } |
66 | ||
f67539c2 TL |
67 | auto stripe_count = std::max<uint64_t>(1U, image->get_stripe_count()); |
68 | chunk_size = (1ull << order) * stripe_count; | |
69 | ||
70 | concurr = std::max<uint64_t>( | |
71 | 1U, g_conf().get_val<uint64_t>("rbd_concurrent_management_ops") / | |
72 | stripe_count); | |
73 | ||
11fdf7f2 TL |
74 | io_status.in_flight = 0; |
75 | io_status.io_error = 0; | |
76 | } | |
77 | ||
78 | int start_io(uint64_t write_offset) | |
79 | { | |
80 | { | |
9f95a23c | 81 | std::lock_guard l{lock}; |
11fdf7f2 TL |
82 | io_status.in_flight++; |
83 | if (io_status.in_flight > concurr) { | |
84 | io_status.in_flight--; | |
85 | return -EINVAL; | |
86 | } | |
87 | } | |
88 | ||
89 | librbd::RBD::AioCompletion *c; | |
90 | c = new librbd::RBD::AioCompletion(this, thick_provision_writer_completion); | |
91 | int r; | |
f67539c2 TL |
92 | r = image->aio_write_zeroes(write_offset, chunk_size, c, |
93 | RBD_WRITE_ZEROES_FLAG_THICK_PROVISION, | |
94 | LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL); | |
11fdf7f2 | 95 | if (r < 0) { |
9f95a23c | 96 | std::lock_guard l{lock}; |
11fdf7f2 TL |
97 | io_status.io_error = r; |
98 | } | |
99 | return r; | |
100 | } | |
101 | ||
102 | int wait_for(uint64_t max) { | |
9f95a23c | 103 | std::unique_lock l{lock}; |
11fdf7f2 TL |
104 | int r = io_status.io_error; |
105 | ||
106 | while (io_status.in_flight > max) { | |
9f95a23c | 107 | cond.wait_for(l, 200ms); |
11fdf7f2 TL |
108 | } |
109 | return r; | |
110 | } | |
111 | }; | |
112 | ||
113 | void thick_provision_writer_completion(rbd_completion_t rc, void *pc) { | |
114 | librbd::RBD::AioCompletion *ac = (librbd::RBD::AioCompletion *)rc; | |
115 | thick_provision_writer *tc = static_cast<thick_provision_writer *>(pc); | |
116 | ||
117 | int r = ac->get_return_value(); | |
9f95a23c | 118 | tc->lock.lock(); |
11fdf7f2 TL |
119 | if (r < 0 && tc->io_status.io_error >= 0) { |
120 | tc->io_status.io_error = r; | |
121 | } | |
122 | tc->io_status.in_flight--; | |
9f95a23c TL |
123 | tc->cond.notify_all(); |
124 | tc->lock.unlock(); | |
11fdf7f2 TL |
125 | ac->release(); |
126 | } | |
127 | ||
128 | int write_data(librbd::Image &image, librbd::ImageOptions &opts, | |
129 | bool no_progress) { | |
130 | uint64_t image_size; | |
131 | int r = 0; | |
132 | utils::ProgressContext pc("Thick provisioning", no_progress); | |
133 | ||
134 | if (image.size(&image_size) != 0) { | |
135 | return -EINVAL; | |
136 | } | |
137 | ||
138 | thick_provision_writer tpw(&image, opts); | |
139 | uint64_t off; | |
140 | uint64_t i; | |
141 | for (off = 0; off < image_size;) { | |
142 | i = 0; | |
143 | while (i < tpw.concurr && off < image_size) { | |
144 | tpw.wait_for(tpw.concurr - 1); | |
145 | r = tpw.start_io(off); | |
146 | if (r != 0) { | |
147 | goto err_writesame; | |
148 | } | |
149 | ++i; | |
150 | off += tpw.chunk_size; | |
92f5a8d4 TL |
151 | if(off > image_size) { |
152 | off = image_size; | |
153 | } | |
11fdf7f2 TL |
154 | pc.update_progress(off, image_size); |
155 | } | |
156 | } | |
157 | ||
158 | tpw.wait_for(0); | |
159 | r = image.flush(); | |
160 | if (r < 0) { | |
161 | std::cerr << "rbd: failed to flush at the end: " << cpp_strerror(r) | |
162 | << std::endl; | |
163 | goto err_writesame; | |
164 | } | |
165 | pc.finish(); | |
166 | ||
167 | return r; | |
168 | ||
169 | err_writesame: | |
170 | tpw.wait_for(0); | |
171 | pc.fail(); | |
172 | ||
173 | return r; | |
174 | } | |
175 | ||
176 | int thick_write(const std::string &image_name,librados::IoCtx &io_ctx, | |
177 | librbd::ImageOptions &opts, bool no_progress) { | |
178 | int r = 0; | |
179 | librbd::Image image; | |
180 | ||
181 | // To prevent writesame from discarding data, thick_write sets | |
182 | // the rbd_discard_on_zeroed_write_same option to false. | |
183 | ceph_assert(g_ceph_context != nullptr); | |
184 | r = g_conf().set_val("rbd_discard_on_zeroed_write_same", "false"); | |
185 | ceph_assert(r == 0); | |
186 | r = utils::open_image(io_ctx, image_name, false, &image); | |
187 | if (r < 0) { | |
188 | return r; | |
189 | } | |
190 | ||
191 | r = write_data(image, opts, no_progress); | |
192 | ||
193 | image.close(); | |
194 | ||
195 | return r; | |
196 | } | |
197 | ||
198 | int execute(const po::variables_map &vm, | |
199 | const std::vector<std::string> &ceph_global_init_args) { | |
7c673cae FG |
200 | size_t arg_index = 0; |
201 | std::string pool_name; | |
11fdf7f2 | 202 | std::string namespace_name; |
7c673cae FG |
203 | std::string image_name; |
204 | std::string snap_name; | |
205 | int r = utils::get_pool_image_snapshot_names( | |
11fdf7f2 TL |
206 | vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, |
207 | &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE, | |
208 | utils::SPEC_VALIDATION_FULL); | |
7c673cae FG |
209 | if (r < 0) { |
210 | return r; | |
211 | } | |
212 | ||
213 | librbd::ImageOptions opts; | |
214 | r = utils::get_image_options(vm, true, &opts); | |
215 | if (r < 0) { | |
216 | return r; | |
217 | } | |
218 | ||
219 | uint64_t size; | |
220 | r = utils::get_image_size(vm, &size); | |
221 | if (r < 0) { | |
222 | return r; | |
223 | } | |
224 | ||
225 | librados::Rados rados; | |
226 | librados::IoCtx io_ctx; | |
11fdf7f2 | 227 | r = utils::init(pool_name, namespace_name, &rados, &io_ctx); |
7c673cae FG |
228 | if (r < 0) { |
229 | return r; | |
230 | } | |
231 | ||
232 | librbd::RBD rbd; | |
233 | r = do_create(rbd, io_ctx, image_name.c_str(), size, opts); | |
11fdf7f2 TL |
234 | if (!namespace_name.empty() && r == -ENOENT) { |
235 | std::cerr << "rbd: namespace not found - it must be created with " | |
236 | << "'rbd namespace create' before creating an image." | |
237 | << std::endl; | |
238 | return r; | |
239 | } else if (r < 0) { | |
7c673cae FG |
240 | std::cerr << "rbd: create error: " << cpp_strerror(r) << std::endl; |
241 | return r; | |
242 | } | |
11fdf7f2 TL |
243 | |
244 | if (vm.count(at::IMAGE_THICK_PROVISION) && vm[at::IMAGE_THICK_PROVISION].as<bool>()) { | |
245 | r = thick_write(image_name, io_ctx, opts, vm[at::NO_PROGRESS].as<bool>()); | |
246 | if (r < 0) { | |
247 | std::cerr << "rbd: image created but error encountered during thick provisioning: " | |
248 | << cpp_strerror(r) << std::endl; | |
249 | return r; | |
250 | } | |
251 | } | |
7c673cae FG |
252 | return 0; |
253 | } | |
254 | ||
255 | Shell::Action action( | |
256 | {"create"}, {}, "Create an empty image.", at::get_long_features_help(), | |
257 | &get_arguments, &execute); | |
258 | ||
259 | } // namespace create | |
260 | } // namespace action | |
261 | } // namespace rbd |