]>
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" | |
7 | #include "common/errno.h" | |
8 | #include <iostream> | |
9 | #include <boost/program_options.hpp> | |
11fdf7f2 TL |
10 | #include "common/Cond.h" |
11 | #include "common/Mutex.h" | |
7c673cae FG |
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); | |
11fdf7f2 TL |
30 | options->add_options() |
31 | (at::IMAGE_THICK_PROVISION.c_str(), po::bool_switch(), "fully allocate storage and zero image"); | |
7c673cae | 32 | at::add_size_option(options); |
11fdf7f2 | 33 | at::add_no_progress_option(options); |
7c673cae FG |
34 | } |
35 | ||
11fdf7f2 TL |
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 | 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 |