]> git.proxmox.com Git - ceph.git/blame - ceph/src/librbd/Operations.cc
update sources to v12.2.3
[ceph.git] / ceph / src / librbd / Operations.cc
CommitLineData
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 "cls/rbd/cls_rbd_types.h"
5#include "librbd/Operations.h"
6#include "common/dout.h"
7#include "common/errno.h"
8#include "common/WorkQueue.h"
9
10#include "librbd/ExclusiveLock.h"
11#include "librbd/ImageCtx.h"
12#include "librbd/ImageState.h"
13#include "librbd/ImageWatcher.h"
14#include "librbd/ObjectMap.h"
b32b8144 15#include "librbd/Types.h"
7c673cae
FG
16#include "librbd/Utils.h"
17#include "librbd/journal/DisabledPolicy.h"
18#include "librbd/journal/StandardPolicy.h"
19#include "librbd/operation/DisableFeaturesRequest.h"
20#include "librbd/operation/EnableFeaturesRequest.h"
21#include "librbd/operation/FlattenRequest.h"
22#include "librbd/operation/MetadataRemoveRequest.h"
23#include "librbd/operation/MetadataSetRequest.h"
24#include "librbd/operation/ObjectMapIterate.h"
25#include "librbd/operation/RebuildObjectMapRequest.h"
26#include "librbd/operation/RenameRequest.h"
27#include "librbd/operation/ResizeRequest.h"
28#include "librbd/operation/SnapshotCreateRequest.h"
29#include "librbd/operation/SnapshotProtectRequest.h"
30#include "librbd/operation/SnapshotRemoveRequest.h"
31#include "librbd/operation/SnapshotRenameRequest.h"
32#include "librbd/operation/SnapshotRollbackRequest.h"
33#include "librbd/operation/SnapshotUnprotectRequest.h"
34#include "librbd/operation/SnapshotLimitRequest.h"
35#include <set>
36#include <boost/bind.hpp>
37#include <boost/scope_exit.hpp>
38
39#define dout_subsys ceph_subsys_rbd
40#undef dout_prefix
41#define dout_prefix *_dout << "librbd::Operations: "
42
43namespace librbd {
44
45namespace {
46
47template <typename I>
48struct C_NotifyUpdate : public Context {
49 I &image_ctx;
50 Context *on_finish;
51 bool notified = false;
52
53 C_NotifyUpdate(I &image_ctx, Context *on_finish)
54 : image_ctx(image_ctx), on_finish(on_finish) {
55 }
56
57 void complete(int r) override {
58 CephContext *cct = image_ctx.cct;
59 if (notified) {
60 if (r == -ETIMEDOUT) {
61 // don't fail the op if a peer fails to get the update notification
62 lderr(cct) << "update notification timed-out" << dendl;
63 r = 0;
64 } else if (r == -ENOENT) {
65 // don't fail if header is missing (e.g. v1 image rename)
66 ldout(cct, 5) << "update notification on missing header" << dendl;
67 r = 0;
68 } else if (r < 0) {
69 lderr(cct) << "update notification failed: " << cpp_strerror(r)
70 << dendl;
71 }
72 Context::complete(r);
73 return;
74 }
75
76 if (r < 0) {
77 // op failed -- no need to send update notification
78 Context::complete(r);
79 return;
80 }
81
82 notified = true;
83 image_ctx.notify_update(this);
84 }
85 void finish(int r) override {
86 on_finish->complete(r);
87 }
88};
89
90template <typename I>
91struct C_InvokeAsyncRequest : public Context {
92 /**
93 * @verbatim
94 *
95 * <start>
96 * |
97 * . . . . . . | . . . . . . . . . . . . . . . . . .
98 * . . | . .
99 * . v v v .
100 * . REFRESH_IMAGE (skip if not needed) .
101 * . | .
102 * . v .
103 * . ACQUIRE_LOCK (skip if exclusive lock .
104 * . | disabled or has lock) .
105 * . | .
106 * . /--------/ \--------\ . . . . . . . . . . . . .
107 * . | | .
108 * . v v .
109 * LOCAL_REQUEST REMOTE_REQUEST
110 * | |
111 * | |
112 * \--------\ /--------/
113 * |
114 * v
115 * <finish>
116 *
117 * @endverbatim
118 */
119
120 I &image_ctx;
121 std::string request_type;
122 bool permit_snapshot;
123 boost::function<void(Context*)> local;
124 boost::function<void(Context*)> remote;
125 std::set<int> filter_error_codes;
126 Context *on_finish;
127 bool request_lock = false;
128
129 C_InvokeAsyncRequest(I &image_ctx, const std::string& request_type,
130 bool permit_snapshot,
131 const boost::function<void(Context*)>& local,
132 const boost::function<void(Context*)>& remote,
133 const std::set<int> &filter_error_codes,
134 Context *on_finish)
135 : image_ctx(image_ctx), request_type(request_type),
136 permit_snapshot(permit_snapshot), local(local), remote(remote),
137 filter_error_codes(filter_error_codes), on_finish(on_finish) {
138 }
139
140 void send() {
141 send_refresh_image();
142 }
143
144 void send_refresh_image() {
145 if (!image_ctx.state->is_refresh_required()) {
146 send_acquire_exclusive_lock();
147 return;
148 }
149
150 CephContext *cct = image_ctx.cct;
151 ldout(cct, 20) << __func__ << dendl;
152
153 Context *ctx = util::create_context_callback<
154 C_InvokeAsyncRequest<I>,
155 &C_InvokeAsyncRequest<I>::handle_refresh_image>(this);
156 image_ctx.state->refresh(ctx);
157 }
158
159 void handle_refresh_image(int r) {
160 CephContext *cct = image_ctx.cct;
161 ldout(cct, 20) << __func__ << ": r=" << r << dendl;
162
163 if (r < 0) {
164 lderr(cct) << "failed to refresh image: " << cpp_strerror(r) << dendl;
165 complete(r);
166 return;
167 }
168
169 send_acquire_exclusive_lock();
170 }
171
172 void send_acquire_exclusive_lock() {
173 // context can complete before owner_lock is unlocked
174 RWLock &owner_lock(image_ctx.owner_lock);
175 owner_lock.get_read();
176 image_ctx.snap_lock.get_read();
177 if (image_ctx.read_only ||
178 (!permit_snapshot && image_ctx.snap_id != CEPH_NOSNAP)) {
179 image_ctx.snap_lock.put_read();
180 owner_lock.put_read();
181 complete(-EROFS);
182 return;
183 }
184 image_ctx.snap_lock.put_read();
185
186 if (image_ctx.exclusive_lock == nullptr) {
187 send_local_request();
188 owner_lock.put_read();
189 return;
190 } else if (image_ctx.image_watcher == nullptr) {
191 owner_lock.put_read();
192 complete(-EROFS);
193 return;
194 }
195
7c673cae 196 if (image_ctx.exclusive_lock->is_lock_owner() &&
31f18b77 197 image_ctx.exclusive_lock->accept_requests()) {
7c673cae
FG
198 send_local_request();
199 owner_lock.put_read();
200 return;
201 }
202
203 CephContext *cct = image_ctx.cct;
204 ldout(cct, 20) << __func__ << dendl;
205
206 Context *ctx = util::create_async_context_callback(
207 image_ctx, util::create_context_callback<
208 C_InvokeAsyncRequest<I>,
209 &C_InvokeAsyncRequest<I>::handle_acquire_exclusive_lock>(this));
210
211 if (request_lock) {
212 // current lock owner doesn't support op -- try to perform
213 // the action locally
214 request_lock = false;
215 image_ctx.exclusive_lock->acquire_lock(ctx);
216 } else {
217 image_ctx.exclusive_lock->try_acquire_lock(ctx);
218 }
219 owner_lock.put_read();
220 }
221
222 void handle_acquire_exclusive_lock(int r) {
223 CephContext *cct = image_ctx.cct;
224 ldout(cct, 20) << __func__ << ": r=" << r << dendl;
225
226 if (r < 0) {
227 complete(-EROFS);
228 return;
229 }
230
231 // context can complete before owner_lock is unlocked
232 RWLock &owner_lock(image_ctx.owner_lock);
233 owner_lock.get_read();
234 if (image_ctx.exclusive_lock->is_lock_owner()) {
235 send_local_request();
236 owner_lock.put_read();
237 return;
238 }
239
240 send_remote_request();
241 owner_lock.put_read();
242 }
243
244 void send_remote_request() {
245 assert(image_ctx.owner_lock.is_locked());
246
247 CephContext *cct = image_ctx.cct;
248 ldout(cct, 20) << __func__ << dendl;
249
250 Context *ctx = util::create_context_callback<
251 C_InvokeAsyncRequest<I>, &C_InvokeAsyncRequest<I>::handle_remote_request>(
252 this);
253 remote(ctx);
254 }
255
256 void handle_remote_request(int r) {
257 CephContext *cct = image_ctx.cct;
258 ldout(cct, 20) << __func__ << ": r=" << r << dendl;
259
260 if (r == -EOPNOTSUPP) {
261 ldout(cct, 5) << request_type << " not supported by current lock owner"
262 << dendl;
263 request_lock = true;
264 send_refresh_image();
265 return;
266 } else if (r != -ETIMEDOUT && r != -ERESTART) {
267 image_ctx.state->handle_update_notification();
268
269 complete(r);
270 return;
271 }
272
273 ldout(cct, 5) << request_type << " timed out notifying lock owner"
274 << dendl;
275 send_refresh_image();
276 }
277
278 void send_local_request() {
279 assert(image_ctx.owner_lock.is_locked());
280
281 CephContext *cct = image_ctx.cct;
282 ldout(cct, 20) << __func__ << dendl;
283
284 Context *ctx = util::create_async_context_callback(
285 image_ctx, util::create_context_callback<
286 C_InvokeAsyncRequest<I>,
287 &C_InvokeAsyncRequest<I>::handle_local_request>(this));
288 local(ctx);
289 }
290
291 void handle_local_request(int r) {
292 CephContext *cct = image_ctx.cct;
293 ldout(cct, 20) << __func__ << ": r=" << r << dendl;
294
295 if (r == -ERESTART) {
296 send_refresh_image();
297 return;
298 }
299 complete(r);
300 }
301
302 void finish(int r) override {
303 if (filter_error_codes.count(r) != 0) {
304 r = 0;
305 }
306 on_finish->complete(r);
307 }
308};
309
310template <typename I>
311bool needs_invalidate(I& image_ctx, uint64_t object_no,
312 uint8_t current_state, uint8_t new_state) {
313 if ( (current_state == OBJECT_EXISTS ||
314 current_state == OBJECT_EXISTS_CLEAN) &&
315 (new_state == OBJECT_NONEXISTENT ||
316 new_state == OBJECT_PENDING)) {
317 return false;
318 }
319 return true;
320}
321
322} // anonymous namespace
323
324template <typename I>
325Operations<I>::Operations(I &image_ctx)
326 : m_image_ctx(image_ctx), m_async_request_seq(0) {
327}
328
329template <typename I>
330int Operations<I>::flatten(ProgressContext &prog_ctx) {
331 CephContext *cct = m_image_ctx.cct;
332 ldout(cct, 20) << "flatten" << dendl;
333
334 int r = m_image_ctx.state->refresh_if_required();
335 if (r < 0) {
336 return r;
337 }
338
339 if (m_image_ctx.read_only) {
340 return -EROFS;
341 }
342
343 {
344 RWLock::RLocker parent_locker(m_image_ctx.parent_lock);
345 if (m_image_ctx.parent_md.spec.pool_id == -1) {
346 lderr(cct) << "image has no parent" << dendl;
347 return -EINVAL;
348 }
349 }
350
351 uint64_t request_id = ++m_async_request_seq;
352 r = invoke_async_request("flatten", false,
353 boost::bind(&Operations<I>::execute_flatten, this,
354 boost::ref(prog_ctx), _1),
355 boost::bind(&ImageWatcher<I>::notify_flatten,
356 m_image_ctx.image_watcher, request_id,
357 boost::ref(prog_ctx), _1));
358
359 if (r < 0 && r != -EINVAL) {
360 return r;
361 }
362 ldout(cct, 20) << "flatten finished" << dendl;
363 return 0;
364}
365
366template <typename I>
367void Operations<I>::execute_flatten(ProgressContext &prog_ctx,
368 Context *on_finish) {
369 assert(m_image_ctx.owner_lock.is_locked());
370 assert(m_image_ctx.exclusive_lock == nullptr ||
371 m_image_ctx.exclusive_lock->is_lock_owner());
372
373 CephContext *cct = m_image_ctx.cct;
374 ldout(cct, 20) << "flatten" << dendl;
375
376 if (m_image_ctx.read_only) {
377 on_finish->complete(-EROFS);
378 return;
379 }
380
381 m_image_ctx.snap_lock.get_read();
382 m_image_ctx.parent_lock.get_read();
383
384 // can't flatten a non-clone
385 if (m_image_ctx.parent_md.spec.pool_id == -1) {
386 lderr(cct) << "image has no parent" << dendl;
387 m_image_ctx.parent_lock.put_read();
388 m_image_ctx.snap_lock.put_read();
389 on_finish->complete(-EINVAL);
390 return;
391 }
392 if (m_image_ctx.snap_id != CEPH_NOSNAP) {
393 lderr(cct) << "snapshots cannot be flattened" << dendl;
394 m_image_ctx.parent_lock.put_read();
395 m_image_ctx.snap_lock.put_read();
396 on_finish->complete(-EROFS);
397 return;
398 }
399
400 ::SnapContext snapc = m_image_ctx.snapc;
401 assert(m_image_ctx.parent != NULL);
402
403 uint64_t overlap;
404 int r = m_image_ctx.get_parent_overlap(CEPH_NOSNAP, &overlap);
405 assert(r == 0);
406 assert(overlap <= m_image_ctx.size);
407
408 uint64_t object_size = m_image_ctx.get_object_size();
409 uint64_t overlap_objects = Striper::get_num_objects(m_image_ctx.layout,
410 overlap);
411
412 m_image_ctx.parent_lock.put_read();
413 m_image_ctx.snap_lock.put_read();
414
415 operation::FlattenRequest<I> *req = new operation::FlattenRequest<I>(
416 m_image_ctx, new C_NotifyUpdate<I>(m_image_ctx, on_finish), object_size,
417 overlap_objects, snapc, prog_ctx);
418 req->send();
419}
420
421template <typename I>
422int Operations<I>::rebuild_object_map(ProgressContext &prog_ctx) {
423 CephContext *cct = m_image_ctx.cct;
424 ldout(cct, 10) << "rebuild_object_map" << dendl;
425
426 int r = m_image_ctx.state->refresh_if_required();
427 if (r < 0) {
428 return r;
429 }
430
431 uint64_t request_id = ++m_async_request_seq;
432 r = invoke_async_request("rebuild object map", true,
433 boost::bind(&Operations<I>::execute_rebuild_object_map,
434 this, boost::ref(prog_ctx), _1),
435 boost::bind(&ImageWatcher<I>::notify_rebuild_object_map,
436 m_image_ctx.image_watcher, request_id,
437 boost::ref(prog_ctx), _1));
438
439 ldout(cct, 10) << "rebuild object map finished" << dendl;
440 if (r < 0) {
441 return r;
442 }
443 return 0;
444}
445
446template <typename I>
447void Operations<I>::execute_rebuild_object_map(ProgressContext &prog_ctx,
448 Context *on_finish) {
449 assert(m_image_ctx.owner_lock.is_locked());
450 assert(m_image_ctx.exclusive_lock == nullptr ||
451 m_image_ctx.exclusive_lock->is_lock_owner());
452
453 CephContext *cct = m_image_ctx.cct;
454 ldout(cct, 5) << this << " " << __func__ << dendl;
455
456 if (m_image_ctx.read_only) {
457 on_finish->complete(-EROFS);
458 return;
459 }
460 if (!m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP)) {
461 lderr(cct) << "image must support object-map feature" << dendl;
462 on_finish->complete(-EINVAL);
463 return;
464 }
465
466 operation::RebuildObjectMapRequest<I> *req =
467 new operation::RebuildObjectMapRequest<I>(
468 m_image_ctx, new C_NotifyUpdate<I>(m_image_ctx, on_finish), prog_ctx);
469 req->send();
470}
471
472template <typename I>
473int Operations<I>::check_object_map(ProgressContext &prog_ctx) {
474 CephContext *cct = m_image_ctx.cct;
475 ldout(cct, 5) << this << " " << __func__ << dendl;
476 int r = m_image_ctx.state->refresh_if_required();
477 if (r < 0) {
478 return r;
479 }
480
481 r = invoke_async_request("check object map", true,
482 boost::bind(&Operations<I>::check_object_map, this,
483 boost::ref(prog_ctx), _1),
484 [] (Context *c) { c->complete(-EOPNOTSUPP); });
485
486 return r;
487}
488
489template <typename I>
490void Operations<I>::object_map_iterate(ProgressContext &prog_ctx,
491 operation::ObjectIterateWork<I> handle_mismatch,
492 Context *on_finish) {
493 assert(m_image_ctx.owner_lock.is_locked());
494 assert(m_image_ctx.exclusive_lock == nullptr ||
495 m_image_ctx.exclusive_lock->is_lock_owner());
496
497 if (!m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP)) {
498 on_finish->complete(-EINVAL);
499 return;
500 }
501
502 operation::ObjectMapIterateRequest<I> *req =
503 new operation::ObjectMapIterateRequest<I>(m_image_ctx, on_finish,
504 prog_ctx, handle_mismatch);
505 req->send();
506}
507
508template <typename I>
509void Operations<I>::check_object_map(ProgressContext &prog_ctx,
510 Context *on_finish) {
511 object_map_iterate(prog_ctx, needs_invalidate, on_finish);
512}
513
514template <typename I>
515int Operations<I>::rename(const char *dstname) {
516 CephContext *cct = m_image_ctx.cct;
517 ldout(cct, 5) << this << " " << __func__ << ": dest_name=" << dstname
518 << dendl;
519
520 int r = librbd::detect_format(m_image_ctx.md_ctx, dstname, NULL, NULL);
521 if (r < 0 && r != -ENOENT) {
522 lderr(cct) << "error checking for existing image called "
523 << dstname << ":" << cpp_strerror(r) << dendl;
524 return r;
525 }
526 if (r == 0) {
527 lderr(cct) << "rbd image " << dstname << " already exists" << dendl;
528 return -EEXIST;
529 }
530
531 if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) {
532 r = invoke_async_request("rename", true,
533 boost::bind(&Operations<I>::execute_rename, this,
534 dstname, _1),
535 boost::bind(&ImageWatcher<I>::notify_rename,
536 m_image_ctx.image_watcher, dstname,
537 _1));
538 if (r < 0 && r != -EEXIST) {
539 return r;
540 }
541 } else {
7c673cae 542 C_SaferCond cond_ctx;
b32b8144
FG
543 {
544 RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
545 execute_rename(dstname, &cond_ctx);
546 }
7c673cae
FG
547
548 r = cond_ctx.wait();
549 if (r < 0) {
550 return r;
551 }
552 }
553
554 m_image_ctx.set_image_name(dstname);
555 return 0;
556}
557
558template <typename I>
559void Operations<I>::execute_rename(const std::string &dest_name,
560 Context *on_finish) {
561 assert(m_image_ctx.owner_lock.is_locked());
562 if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) {
563 assert(m_image_ctx.exclusive_lock == nullptr ||
564 m_image_ctx.exclusive_lock->is_lock_owner());
565 }
566
567 m_image_ctx.snap_lock.get_read();
568 if (m_image_ctx.name == dest_name) {
569 m_image_ctx.snap_lock.put_read();
570 on_finish->complete(-EEXIST);
571 return;
572 }
573 m_image_ctx.snap_lock.put_read();
574
575 CephContext *cct = m_image_ctx.cct;
576 ldout(cct, 5) << this << " " << __func__ << ": dest_name=" << dest_name
577 << dendl;
578
579 if (m_image_ctx.old_format) {
580 // unregister watch before and register back after rename
581 on_finish = new C_NotifyUpdate<I>(m_image_ctx, on_finish);
582 on_finish = new FunctionContext([this, on_finish](int r) {
583 if (m_image_ctx.old_format) {
584 m_image_ctx.image_watcher->set_oid(m_image_ctx.header_oid);
585 }
586 m_image_ctx.image_watcher->register_watch(on_finish);
587 });
588 on_finish = new FunctionContext([this, dest_name, on_finish](int r) {
b32b8144 589 RWLock::RLocker owner_locker(m_image_ctx.owner_lock);
7c673cae
FG
590 operation::RenameRequest<I> *req = new operation::RenameRequest<I>(
591 m_image_ctx, on_finish, dest_name);
592 req->send();
593 });
594 m_image_ctx.image_watcher->unregister_watch(on_finish);
595 return;
596 }
597 operation::RenameRequest<I> *req = new operation::RenameRequest<I>(
598 m_image_ctx, on_finish, dest_name);
599 req->send();
600}
601
602template <typename I>
603int Operations<I>::resize(uint64_t size, bool allow_shrink, ProgressContext& prog_ctx) {
604 CephContext *cct = m_image_ctx.cct;
605
606 m_image_ctx.snap_lock.get_read();
607 ldout(cct, 5) << this << " " << __func__ << ": "
608 << "size=" << m_image_ctx.size << ", "
609 << "new_size=" << size << dendl;
610 m_image_ctx.snap_lock.put_read();
611
612 int r = m_image_ctx.state->refresh_if_required();
613 if (r < 0) {
614 return r;
615 }
616
617 if (m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP) &&
618 !ObjectMap<>::is_compatible(m_image_ctx.layout, size)) {
619 lderr(cct) << "New size not compatible with object map" << dendl;
620 return -EINVAL;
621 }
622
623 uint64_t request_id = ++m_async_request_seq;
624 r = invoke_async_request("resize", false,
625 boost::bind(&Operations<I>::execute_resize, this,
626 size, allow_shrink, boost::ref(prog_ctx), _1, 0),
627 boost::bind(&ImageWatcher<I>::notify_resize,
628 m_image_ctx.image_watcher, request_id,
629 size, allow_shrink, boost::ref(prog_ctx), _1));
630
631 m_image_ctx.perfcounter->inc(l_librbd_resize);
632 ldout(cct, 2) << "resize finished" << dendl;
633 return r;
634}
635
636template <typename I>
637void Operations<I>::execute_resize(uint64_t size, bool allow_shrink, ProgressContext &prog_ctx,
638 Context *on_finish,
639 uint64_t journal_op_tid) {
640 assert(m_image_ctx.owner_lock.is_locked());
641 assert(m_image_ctx.exclusive_lock == nullptr ||
642 m_image_ctx.exclusive_lock->is_lock_owner());
643
644 CephContext *cct = m_image_ctx.cct;
645 m_image_ctx.snap_lock.get_read();
646 ldout(cct, 5) << this << " " << __func__ << ": "
647 << "size=" << m_image_ctx.size << ", "
648 << "new_size=" << size << dendl;
649
650 if (m_image_ctx.snap_id != CEPH_NOSNAP || m_image_ctx.read_only) {
651 m_image_ctx.snap_lock.put_read();
652 on_finish->complete(-EROFS);
653 return;
654 } else if (m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP,
655 m_image_ctx.snap_lock) &&
656 !ObjectMap<>::is_compatible(m_image_ctx.layout, size)) {
657 m_image_ctx.snap_lock.put_read();
658 on_finish->complete(-EINVAL);
659 return;
660 }
661 m_image_ctx.snap_lock.put_read();
662
663 operation::ResizeRequest<I> *req = new operation::ResizeRequest<I>(
664 m_image_ctx, new C_NotifyUpdate<I>(m_image_ctx, on_finish), size, allow_shrink,
665 prog_ctx, journal_op_tid, false);
666 req->send();
667}
668
669template <typename I>
670int Operations<I>::snap_create(const cls::rbd::SnapshotNamespace &snap_namespace,
671 const char *snap_name) {
672 if (m_image_ctx.read_only) {
673 return -EROFS;
674 }
675
676 int r = m_image_ctx.state->refresh_if_required();
677 if (r < 0) {
678 return r;
679 }
680
681 C_SaferCond ctx;
682 snap_create(snap_namespace, snap_name, &ctx);
683 r = ctx.wait();
684
685 if (r < 0) {
686 return r;
687 }
688
689 m_image_ctx.perfcounter->inc(l_librbd_snap_create);
690 return r;
691}
692
693template <typename I>
694void Operations<I>::snap_create(const cls::rbd::SnapshotNamespace &snap_namespace,
695 const char *snap_name,
696 Context *on_finish) {
697 CephContext *cct = m_image_ctx.cct;
698 ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
699 << dendl;
700
701 if (m_image_ctx.read_only) {
702 on_finish->complete(-EROFS);
703 return;
704 }
705
706 m_image_ctx.snap_lock.get_read();
707 if (m_image_ctx.get_snap_id(snap_namespace, snap_name) != CEPH_NOSNAP) {
708 m_image_ctx.snap_lock.put_read();
709 on_finish->complete(-EEXIST);
710 return;
711 }
712 m_image_ctx.snap_lock.put_read();
713
714 C_InvokeAsyncRequest<I> *req = new C_InvokeAsyncRequest<I>(
715 m_image_ctx, "snap_create", true,
716 boost::bind(&Operations<I>::execute_snap_create, this, snap_namespace, snap_name,
717 _1, 0, false),
718 boost::bind(&ImageWatcher<I>::notify_snap_create, m_image_ctx.image_watcher,
719 snap_namespace, snap_name, _1),
720 {-EEXIST}, on_finish);
721 req->send();
722}
723
724template <typename I>
725void Operations<I>::execute_snap_create(const cls::rbd::SnapshotNamespace &snap_namespace,
726 const std::string &snap_name,
727 Context *on_finish,
728 uint64_t journal_op_tid,
729 bool skip_object_map) {
730 assert(m_image_ctx.owner_lock.is_locked());
731 assert(m_image_ctx.exclusive_lock == nullptr ||
732 m_image_ctx.exclusive_lock->is_lock_owner());
733
734 CephContext *cct = m_image_ctx.cct;
735 ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
736 << dendl;
737
738 m_image_ctx.snap_lock.get_read();
739 if (m_image_ctx.get_snap_id(snap_namespace, snap_name) != CEPH_NOSNAP) {
740 m_image_ctx.snap_lock.put_read();
741 on_finish->complete(-EEXIST);
742 return;
743 }
744 m_image_ctx.snap_lock.put_read();
745
746 operation::SnapshotCreateRequest<I> *req =
747 new operation::SnapshotCreateRequest<I>(
748 m_image_ctx, new C_NotifyUpdate<I>(m_image_ctx, on_finish),
749 snap_namespace, snap_name, journal_op_tid, skip_object_map);
750 req->send();
751}
752
753template <typename I>
754int Operations<I>::snap_rollback(const cls::rbd::SnapshotNamespace& snap_namespace,
755 const char *snap_name,
756 ProgressContext& prog_ctx) {
757 CephContext *cct = m_image_ctx.cct;
758 ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
759 << dendl;
760
761 int r = m_image_ctx.state->refresh_if_required();
762 if (r < 0)
763 return r;
764
b32b8144 765 C_SaferCond cond_ctx;
7c673cae 766 {
b32b8144
FG
767 RWLock::RLocker owner_locker(m_image_ctx.owner_lock);
768 {
769 // need to drop snap_lock before invalidating cache
770 RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
771 if (!m_image_ctx.snap_exists) {
772 return -ENOENT;
773 }
7c673cae 774
b32b8144
FG
775 if (m_image_ctx.snap_id != CEPH_NOSNAP || m_image_ctx.read_only) {
776 return -EROFS;
777 }
778
779 uint64_t snap_id = m_image_ctx.get_snap_id(snap_namespace, snap_name);
780 if (snap_id == CEPH_NOSNAP) {
781 lderr(cct) << "No such snapshot found." << dendl;
782 return -ENOENT;
783 }
7c673cae
FG
784 }
785
b32b8144
FG
786 r = prepare_image_update(false);
787 if (r < 0) {
788 return -EROFS;
7c673cae 789 }
7c673cae 790
b32b8144 791 execute_snap_rollback(snap_namespace, snap_name, prog_ctx, &cond_ctx);
7c673cae
FG
792 }
793
7c673cae
FG
794 r = cond_ctx.wait();
795 if (r < 0) {
796 return r;
797 }
798
799 m_image_ctx.perfcounter->inc(l_librbd_snap_rollback);
800 return r;
801}
802
803template <typename I>
804void Operations<I>::execute_snap_rollback(const cls::rbd::SnapshotNamespace& snap_namespace,
805 const std::string &snap_name,
806 ProgressContext& prog_ctx,
807 Context *on_finish) {
808 assert(m_image_ctx.owner_lock.is_locked());
809 CephContext *cct = m_image_ctx.cct;
810 ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
811 << dendl;
812
813 m_image_ctx.snap_lock.get_read();
814 uint64_t snap_id = m_image_ctx.get_snap_id(snap_namespace, snap_name);
815 if (snap_id == CEPH_NOSNAP) {
816 lderr(cct) << "No such snapshot found." << dendl;
817 m_image_ctx.snap_lock.put_read();
818 on_finish->complete(-ENOENT);
819 return;
820 }
821
822 uint64_t new_size = m_image_ctx.get_image_size(snap_id);
823 m_image_ctx.snap_lock.put_read();
824
825 // async mode used for journal replay
826 operation::SnapshotRollbackRequest<I> *request =
827 new operation::SnapshotRollbackRequest<I>(
828 m_image_ctx, new C_NotifyUpdate<I>(m_image_ctx, on_finish), snap_namespace, snap_name,
829 snap_id, new_size, prog_ctx);
830 request->send();
831}
832
833template <typename I>
834int Operations<I>::snap_remove(const cls::rbd::SnapshotNamespace& snap_namespace,
835 const char *snap_name) {
836 if (m_image_ctx.read_only) {
837 return -EROFS;
838 }
839
840 int r = m_image_ctx.state->refresh_if_required();
841 if (r < 0) {
842 return r;
843 }
844
845 C_SaferCond ctx;
846 snap_remove(snap_namespace, snap_name, &ctx);
847 r = ctx.wait();
848
849 if (r < 0) {
850 return r;
851 }
852
853 m_image_ctx.perfcounter->inc(l_librbd_snap_remove);
854 return 0;
855}
856
857template <typename I>
858void Operations<I>::snap_remove(const cls::rbd::SnapshotNamespace& snap_namespace,
859 const char *snap_name,
860 Context *on_finish) {
861 CephContext *cct = m_image_ctx.cct;
862 ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
863 << dendl;
864
865 if (m_image_ctx.read_only) {
866 on_finish->complete(-EROFS);
867 return;
868 }
869
870 // quickly filter out duplicate ops
871 m_image_ctx.snap_lock.get_read();
872 if (m_image_ctx.get_snap_id(snap_namespace, snap_name) == CEPH_NOSNAP) {
873 m_image_ctx.snap_lock.put_read();
874 on_finish->complete(-ENOENT);
875 return;
876 }
877
878 bool proxy_op = ((m_image_ctx.features & RBD_FEATURE_FAST_DIFF) != 0 ||
879 (m_image_ctx.features & RBD_FEATURE_JOURNALING) != 0);
880 m_image_ctx.snap_lock.put_read();
881
882 if (proxy_op) {
883 C_InvokeAsyncRequest<I> *req = new C_InvokeAsyncRequest<I>(
884 m_image_ctx, "snap_remove", true,
885 boost::bind(&Operations<I>::execute_snap_remove, this, snap_namespace, snap_name, _1),
886 boost::bind(&ImageWatcher<I>::notify_snap_remove, m_image_ctx.image_watcher,
887 snap_namespace, snap_name, _1),
888 {-ENOENT}, on_finish);
889 req->send();
890 } else {
891 RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
892 execute_snap_remove(snap_namespace, snap_name, on_finish);
893 }
894}
895
896template <typename I>
897void Operations<I>::execute_snap_remove(const cls::rbd::SnapshotNamespace& snap_namespace,
898 const std::string &snap_name,
899 Context *on_finish) {
900 assert(m_image_ctx.owner_lock.is_locked());
901 {
902 if ((m_image_ctx.features & RBD_FEATURE_FAST_DIFF) != 0) {
903 assert(m_image_ctx.exclusive_lock == nullptr ||
904 m_image_ctx.exclusive_lock->is_lock_owner());
905 }
906 }
907
908 CephContext *cct = m_image_ctx.cct;
909 ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
910 << dendl;
911
912 m_image_ctx.snap_lock.get_read();
913 uint64_t snap_id = m_image_ctx.get_snap_id(snap_namespace, snap_name);
914 if (snap_id == CEPH_NOSNAP) {
915 lderr(m_image_ctx.cct) << "No such snapshot found." << dendl;
916 m_image_ctx.snap_lock.put_read();
917 on_finish->complete(-ENOENT);
918 return;
919 }
920
921 bool is_protected;
922 int r = m_image_ctx.is_snap_protected(snap_id, &is_protected);
923 if (r < 0) {
924 m_image_ctx.snap_lock.put_read();
925 on_finish->complete(r);
926 return;
927 } else if (is_protected) {
928 lderr(m_image_ctx.cct) << "snapshot is protected" << dendl;
929 m_image_ctx.snap_lock.put_read();
930 on_finish->complete(-EBUSY);
931 return;
932 }
933 m_image_ctx.snap_lock.put_read();
934
935 operation::SnapshotRemoveRequest<I> *req =
936 new operation::SnapshotRemoveRequest<I>(
937 m_image_ctx, new C_NotifyUpdate<I>(m_image_ctx, on_finish),
938 snap_namespace, snap_name, snap_id);
939 req->send();
940}
941
942template <typename I>
943int Operations<I>::snap_rename(const char *srcname, const char *dstname) {
944 CephContext *cct = m_image_ctx.cct;
945 ldout(cct, 5) << this << " " << __func__ << ": "
946 << "snap_name=" << srcname << ", "
947 << "new_snap_name=" << dstname << dendl;
948
949 snapid_t snap_id;
950 if (m_image_ctx.read_only) {
951 return -EROFS;
952 }
953
954 int r = m_image_ctx.state->refresh_if_required();
955 if (r < 0)
956 return r;
957
958 {
959 RWLock::RLocker l(m_image_ctx.snap_lock);
960 snap_id = m_image_ctx.get_snap_id(cls::rbd::UserSnapshotNamespace(), srcname);
961 if (snap_id == CEPH_NOSNAP) {
962 return -ENOENT;
963 }
964 if (m_image_ctx.get_snap_id(cls::rbd::UserSnapshotNamespace(), dstname) != CEPH_NOSNAP) {
965 return -EEXIST;
966 }
967 }
968
969 if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) {
970 r = invoke_async_request("snap_rename", true,
971 boost::bind(&Operations<I>::execute_snap_rename,
972 this, snap_id, dstname, _1),
973 boost::bind(&ImageWatcher<I>::notify_snap_rename,
974 m_image_ctx.image_watcher, snap_id,
975 dstname, _1));
976 if (r < 0 && r != -EEXIST) {
977 return r;
978 }
979 } else {
7c673cae 980 C_SaferCond cond_ctx;
b32b8144
FG
981 {
982 RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
983 execute_snap_rename(snap_id, dstname, &cond_ctx);
984 }
7c673cae
FG
985
986 r = cond_ctx.wait();
987 if (r < 0) {
988 return r;
989 }
990 }
991
992 m_image_ctx.perfcounter->inc(l_librbd_snap_rename);
993 return 0;
994}
995
996template <typename I>
997void Operations<I>::execute_snap_rename(const uint64_t src_snap_id,
998 const std::string &dest_snap_name,
999 Context *on_finish) {
1000 assert(m_image_ctx.owner_lock.is_locked());
1001 if ((m_image_ctx.features & RBD_FEATURE_JOURNALING) != 0) {
1002 assert(m_image_ctx.exclusive_lock == nullptr ||
1003 m_image_ctx.exclusive_lock->is_lock_owner());
1004 }
1005
1006 m_image_ctx.snap_lock.get_read();
1007 if (m_image_ctx.get_snap_id(cls::rbd::UserSnapshotNamespace(),
1008 dest_snap_name) != CEPH_NOSNAP) {
1009 // Renaming is supported for snapshots from user namespace only.
1010 m_image_ctx.snap_lock.put_read();
1011 on_finish->complete(-EEXIST);
1012 return;
1013 }
1014 m_image_ctx.snap_lock.put_read();
1015
1016 CephContext *cct = m_image_ctx.cct;
1017 ldout(cct, 5) << this << " " << __func__ << ": "
1018 << "snap_id=" << src_snap_id << ", "
1019 << "new_snap_name=" << dest_snap_name << dendl;
1020
1021 operation::SnapshotRenameRequest<I> *req =
1022 new operation::SnapshotRenameRequest<I>(
1023 m_image_ctx, new C_NotifyUpdate<I>(m_image_ctx, on_finish), src_snap_id,
1024 dest_snap_name);
1025 req->send();
1026}
1027
1028template <typename I>
1029int Operations<I>::snap_protect(const cls::rbd::SnapshotNamespace& snap_namespace,
1030 const char *snap_name) {
1031 CephContext *cct = m_image_ctx.cct;
1032 ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
1033 << dendl;
1034
1035 if (m_image_ctx.read_only) {
1036 return -EROFS;
1037 }
1038
1039 if (!m_image_ctx.test_features(RBD_FEATURE_LAYERING)) {
1040 lderr(cct) << "image must support layering" << dendl;
1041 return -ENOSYS;
1042 }
1043
1044 int r = m_image_ctx.state->refresh_if_required();
1045 if (r < 0) {
1046 return r;
1047 }
1048
1049 {
1050 RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
1051 bool is_protected;
1052 r = m_image_ctx.is_snap_protected(m_image_ctx.get_snap_id(snap_namespace, snap_name),
1053 &is_protected);
1054 if (r < 0) {
1055 return r;
1056 }
1057
1058 if (is_protected) {
1059 return -EBUSY;
1060 }
1061 }
1062
1063 if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) {
1064 r = invoke_async_request("snap_protect", true,
1065 boost::bind(&Operations<I>::execute_snap_protect,
1066 this, snap_namespace, snap_name, _1),
1067 boost::bind(&ImageWatcher<I>::notify_snap_protect,
1068 m_image_ctx.image_watcher,
1069 snap_namespace, snap_name, _1));
1070 if (r < 0 && r != -EBUSY) {
1071 return r;
1072 }
1073 } else {
7c673cae 1074 C_SaferCond cond_ctx;
b32b8144
FG
1075 {
1076 RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
1077 execute_snap_protect(snap_namespace, snap_name, &cond_ctx);
1078 }
7c673cae
FG
1079
1080 r = cond_ctx.wait();
1081 if (r < 0) {
1082 return r;
1083 }
1084 }
1085 return 0;
1086}
1087
1088template <typename I>
1089void Operations<I>::execute_snap_protect(const cls::rbd::SnapshotNamespace& snap_namespace,
1090 const std::string &snap_name,
1091 Context *on_finish) {
1092 assert(m_image_ctx.owner_lock.is_locked());
1093 if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) {
1094 assert(m_image_ctx.exclusive_lock == nullptr ||
1095 m_image_ctx.exclusive_lock->is_lock_owner());
1096 }
1097
1098 m_image_ctx.snap_lock.get_read();
1099 bool is_protected;
1100 int r = m_image_ctx.is_snap_protected(m_image_ctx.get_snap_id(snap_namespace, snap_name),
1101 &is_protected);
1102 if (r < 0) {
1103 m_image_ctx.snap_lock.put_read();
1104 on_finish->complete(r);
1105 return;
1106 } else if (is_protected) {
1107 m_image_ctx.snap_lock.put_read();
1108 on_finish->complete(-EBUSY);
1109 return;
1110 }
1111 m_image_ctx.snap_lock.put_read();
1112
1113 CephContext *cct = m_image_ctx.cct;
1114 ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
1115 << dendl;
1116
1117 operation::SnapshotProtectRequest<I> *request =
1118 new operation::SnapshotProtectRequest<I>(
1119 m_image_ctx, new C_NotifyUpdate<I>(m_image_ctx, on_finish), snap_namespace, snap_name);
1120 request->send();
1121}
1122
1123template <typename I>
1124int Operations<I>::snap_unprotect(const cls::rbd::SnapshotNamespace& snap_namespace,
1125 const char *snap_name) {
1126 CephContext *cct = m_image_ctx.cct;
1127 ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
1128 << dendl;
1129
1130 if (m_image_ctx.read_only) {
1131 return -EROFS;
1132 }
1133
1134 int r = m_image_ctx.state->refresh_if_required();
1135 if (r < 0) {
1136 return r;
1137 }
1138
1139 {
1140 RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
1141 bool is_unprotected;
1142 r = m_image_ctx.is_snap_unprotected(m_image_ctx.get_snap_id(snap_namespace, snap_name),
1143 &is_unprotected);
1144 if (r < 0) {
1145 return r;
1146 }
1147
1148 if (is_unprotected) {
1149 return -EINVAL;
1150 }
1151 }
1152
1153 if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) {
1154 r = invoke_async_request("snap_unprotect", true,
1155 boost::bind(&Operations<I>::execute_snap_unprotect,
1156 this, snap_namespace, snap_name, _1),
1157 boost::bind(&ImageWatcher<I>::notify_snap_unprotect,
1158 m_image_ctx.image_watcher,
1159 snap_namespace, snap_name, _1));
1160 if (r < 0 && r != -EINVAL) {
1161 return r;
1162 }
1163 } else {
7c673cae 1164 C_SaferCond cond_ctx;
b32b8144
FG
1165 {
1166 RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
1167 execute_snap_unprotect(snap_namespace, snap_name, &cond_ctx);
1168 }
7c673cae
FG
1169
1170 r = cond_ctx.wait();
1171 if (r < 0) {
1172 return r;
1173 }
1174 }
1175 return 0;
1176}
1177
1178template <typename I>
1179void Operations<I>::execute_snap_unprotect(const cls::rbd::SnapshotNamespace& snap_namespace,
1180 const std::string &snap_name,
1181 Context *on_finish) {
1182 assert(m_image_ctx.owner_lock.is_locked());
1183 if (m_image_ctx.test_features(RBD_FEATURE_JOURNALING)) {
1184 assert(m_image_ctx.exclusive_lock == nullptr ||
1185 m_image_ctx.exclusive_lock->is_lock_owner());
1186 }
1187
1188 m_image_ctx.snap_lock.get_read();
1189 bool is_unprotected;
1190 int r = m_image_ctx.is_snap_unprotected(m_image_ctx.get_snap_id(snap_namespace, snap_name),
1191 &is_unprotected);
1192 if (r < 0) {
1193 m_image_ctx.snap_lock.put_read();
1194 on_finish->complete(r);
1195 return;
1196 } else if (is_unprotected) {
1197 m_image_ctx.snap_lock.put_read();
1198 on_finish->complete(-EINVAL);
1199 return;
1200 }
1201 m_image_ctx.snap_lock.put_read();
1202
1203 CephContext *cct = m_image_ctx.cct;
1204 ldout(cct, 5) << this << " " << __func__ << ": snap_name=" << snap_name
1205 << dendl;
1206
1207 operation::SnapshotUnprotectRequest<I> *request =
1208 new operation::SnapshotUnprotectRequest<I>(
1209 m_image_ctx, new C_NotifyUpdate<I>(m_image_ctx, on_finish), snap_namespace, snap_name);
1210 request->send();
1211}
1212
1213template <typename I>
1214int Operations<I>::snap_set_limit(uint64_t limit) {
1215 CephContext *cct = m_image_ctx.cct;
1216 ldout(cct, 5) << this << " " << __func__ << ": limit=" << limit << dendl;
1217
1218 if (m_image_ctx.read_only) {
1219 return -EROFS;
1220 }
1221
1222 int r = m_image_ctx.state->refresh_if_required();
1223 if (r < 0) {
1224 return r;
1225 }
1226
b32b8144 1227 C_SaferCond limit_ctx;
7c673cae
FG
1228 {
1229 RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
b32b8144
FG
1230 r = prepare_image_update(true);
1231 if (r < 0) {
1232 return r;
7c673cae
FG
1233 }
1234
1235 execute_snap_set_limit(limit, &limit_ctx);
7c673cae
FG
1236 }
1237
b32b8144 1238 r = limit_ctx.wait();
7c673cae
FG
1239 return r;
1240}
1241
1242template <typename I>
1243void Operations<I>::execute_snap_set_limit(const uint64_t limit,
1244 Context *on_finish) {
1245 assert(m_image_ctx.owner_lock.is_locked());
1246
1247 CephContext *cct = m_image_ctx.cct;
1248 ldout(cct, 5) << this << " " << __func__ << ": limit=" << limit
1249 << dendl;
1250
1251 operation::SnapshotLimitRequest<I> *request =
1252 new operation::SnapshotLimitRequest<I>(m_image_ctx, on_finish, limit);
1253 request->send();
1254}
1255
1256template <typename I>
1257int Operations<I>::update_features(uint64_t features, bool enabled) {
1258 CephContext *cct = m_image_ctx.cct;
1259 ldout(cct, 5) << this << " " << __func__ << ": features=" << features
1260 << ", enabled=" << enabled << dendl;
1261
1262 int r = m_image_ctx.state->refresh_if_required();
1263 if (r < 0) {
1264 return r;
1265 }
1266
1267 if (m_image_ctx.read_only) {
1268 return -EROFS;
1269 } else if (m_image_ctx.old_format) {
1270 lderr(cct) << "old-format images do not support features" << dendl;
1271 return -EINVAL;
1272 }
1273
1274 uint64_t disable_mask = (RBD_FEATURES_MUTABLE |
1275 RBD_FEATURES_DISABLE_ONLY);
1276 if ((enabled && (features & RBD_FEATURES_MUTABLE) != features) ||
1277 (!enabled && (features & disable_mask) != features)) {
1278 lderr(cct) << "cannot update immutable features" << dendl;
1279 return -EINVAL;
1280 }
1281 if (features == 0) {
1282 lderr(cct) << "update requires at least one feature" << dendl;
1283 return -EINVAL;
1284 }
1285 {
1286 RWLock::RLocker snap_locker(m_image_ctx.snap_lock);
1287 if (enabled && (features & m_image_ctx.features) != 0) {
1288 lderr(cct) << "one or more requested features are already enabled"
1289 << dendl;
1290 return -EINVAL;
1291 }
1292 if (!enabled && (features & ~m_image_ctx.features) != 0) {
1293 lderr(cct) << "one or more requested features are already disabled"
1294 << dendl;
1295 return -EINVAL;
1296 }
1297 }
1298
1299 // if disabling journaling, avoid attempting to open the journal
1300 // when acquiring the exclusive lock in case the journal is corrupt
1301 bool disabling_journal = false;
1302 if (!enabled && ((features & RBD_FEATURE_JOURNALING) != 0)) {
1303 RWLock::WLocker snap_locker(m_image_ctx.snap_lock);
1304 m_image_ctx.set_journal_policy(new journal::DisabledPolicy());
1305 disabling_journal = true;
1306 }
1307 BOOST_SCOPE_EXIT_ALL( (this)(disabling_journal) ) {
1308 if (disabling_journal) {
1309 RWLock::WLocker snap_locker(m_image_ctx.snap_lock);
1310 m_image_ctx.set_journal_policy(
1311 new journal::StandardPolicy<I>(&m_image_ctx));
1312 }
1313 };
1314
1315 r = invoke_async_request("update_features", false,
1316 boost::bind(&Operations<I>::execute_update_features,
1317 this, features, enabled, _1, 0),
1318 boost::bind(&ImageWatcher<I>::notify_update_features,
1319 m_image_ctx.image_watcher, features,
1320 enabled, _1));
1321 ldout(cct, 2) << "update_features finished" << dendl;
1322 return r;
1323}
1324
1325template <typename I>
1326void Operations<I>::execute_update_features(uint64_t features, bool enabled,
1327 Context *on_finish,
1328 uint64_t journal_op_tid) {
1329 assert(m_image_ctx.owner_lock.is_locked());
1330 assert(m_image_ctx.exclusive_lock == nullptr ||
1331 m_image_ctx.exclusive_lock->is_lock_owner());
1332
1333 CephContext *cct = m_image_ctx.cct;
1334 ldout(cct, 5) << this << " " << __func__ << ": features=" << features
1335 << ", enabled=" << enabled << dendl;
1336
1337 if (enabled) {
1338 operation::EnableFeaturesRequest<I> *req =
1339 new operation::EnableFeaturesRequest<I>(
1340 m_image_ctx, on_finish, journal_op_tid, features);
1341 req->send();
1342 } else {
1343 operation::DisableFeaturesRequest<I> *req =
1344 new operation::DisableFeaturesRequest<I>(
1345 m_image_ctx, on_finish, journal_op_tid, features, false);
1346 req->send();
1347 }
1348}
1349
1350template <typename I>
1351int Operations<I>::metadata_set(const std::string &key,
1352 const std::string &value) {
1353 CephContext *cct = m_image_ctx.cct;
1354 ldout(cct, 5) << this << " " << __func__ << ": key=" << key << ", value="
1355 << value << dendl;
1356
b32b8144
FG
1357 std::string config_key;
1358 bool config_override = util::is_metadata_config_override(key, &config_key);
1359 if (config_override) {
7c673cae 1360 // validate config setting
b32b8144 1361 int r = md_config_t().set_val(config_key.c_str(), value);
7c673cae
FG
1362 if (r < 0) {
1363 return r;
1364 }
1365 }
1366
1367 int r = m_image_ctx.state->refresh_if_required();
1368 if (r < 0) {
1369 return r;
1370 }
1371
1372 if (m_image_ctx.read_only) {
1373 return -EROFS;
1374 }
1375
b32b8144 1376 C_SaferCond metadata_ctx;
7c673cae
FG
1377 {
1378 RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
b32b8144
FG
1379 r = prepare_image_update(true);
1380 if (r < 0) {
1381 return r;
7c673cae
FG
1382 }
1383
1384 execute_metadata_set(key, value, &metadata_ctx);
b32b8144
FG
1385 }
1386
1387 r = metadata_ctx.wait();
1388 if (config_override && r >= 0) {
1389 // apply new config key immediately
1390 r = m_image_ctx.state->refresh_if_required();
7c673cae
FG
1391 }
1392
1393 return r;
1394}
1395
1396template <typename I>
1397void Operations<I>::execute_metadata_set(const std::string &key,
1398 const std::string &value,
1399 Context *on_finish) {
1400 assert(m_image_ctx.owner_lock.is_locked());
1401
1402 CephContext *cct = m_image_ctx.cct;
1403 ldout(cct, 5) << this << " " << __func__ << ": key=" << key << ", value="
1404 << value << dendl;
1405
1406 operation::MetadataSetRequest<I> *request =
b32b8144
FG
1407 new operation::MetadataSetRequest<I>(m_image_ctx,
1408 new C_NotifyUpdate<I>(m_image_ctx, on_finish),
1409 key, value);
7c673cae
FG
1410 request->send();
1411}
1412
1413template <typename I>
1414int Operations<I>::metadata_remove(const std::string &key) {
1415 CephContext *cct = m_image_ctx.cct;
1416 ldout(cct, 5) << this << " " << __func__ << ": key=" << key << dendl;
1417
1418 if (m_image_ctx.read_only) {
1419 return -EROFS;
1420 }
1421
1422 int r = m_image_ctx.state->refresh_if_required();
1423 if (r < 0) {
1424 return r;
1425 }
1426
1427 if (m_image_ctx.read_only) {
1428 return -EROFS;
1429 }
1430
d2e6a577
FG
1431 std::string value;
1432 r = cls_client::metadata_get(&m_image_ctx.md_ctx, m_image_ctx.header_oid, key, &value);
1433 if(r < 0)
1434 return r;
1435
b32b8144 1436 C_SaferCond metadata_ctx;
7c673cae
FG
1437 {
1438 RWLock::RLocker owner_lock(m_image_ctx.owner_lock);
b32b8144
FG
1439 r = prepare_image_update(true);
1440 if (r < 0) {
1441 return r;
7c673cae
FG
1442 }
1443
1444 execute_metadata_remove(key, &metadata_ctx);
b32b8144
FG
1445 }
1446
1447 r = metadata_ctx.wait();
1448
1449 std::string config_key;
1450 if (util::is_metadata_config_override(key, &config_key) && r >= 0) {
1451 // apply new config key immediately
1452 r = m_image_ctx.state->refresh_if_required();
7c673cae
FG
1453 }
1454
1455 return r;
1456}
1457
1458template <typename I>
1459void Operations<I>::execute_metadata_remove(const std::string &key,
1460 Context *on_finish) {
1461 assert(m_image_ctx.owner_lock.is_locked());
1462
1463 CephContext *cct = m_image_ctx.cct;
1464 ldout(cct, 5) << this << " " << __func__ << ": key=" << key << dendl;
1465
1466 operation::MetadataRemoveRequest<I> *request =
b32b8144
FG
1467 new operation::MetadataRemoveRequest<I>(
1468 m_image_ctx,
1469 new C_NotifyUpdate<I>(m_image_ctx, on_finish), key);
7c673cae
FG
1470 request->send();
1471}
1472
1473template <typename I>
b32b8144 1474int Operations<I>::prepare_image_update(bool request_lock) {
7c673cae
FG
1475 assert(m_image_ctx.owner_lock.is_locked() &&
1476 !m_image_ctx.owner_lock.is_wlocked());
b32b8144 1477 if (m_image_ctx.image_watcher == nullptr) {
7c673cae
FG
1478 return -EROFS;
1479 }
1480
1481 // need to upgrade to a write lock
7c673cae
FG
1482 C_SaferCond ctx;
1483 m_image_ctx.owner_lock.put_read();
b32b8144 1484 bool attempting_lock = false;
7c673cae
FG
1485 {
1486 RWLock::WLocker owner_locker(m_image_ctx.owner_lock);
1487 if (m_image_ctx.exclusive_lock != nullptr &&
1488 (!m_image_ctx.exclusive_lock->is_lock_owner() ||
31f18b77 1489 !m_image_ctx.exclusive_lock->accept_requests())) {
b32b8144
FG
1490
1491 attempting_lock = true;
1492 m_image_ctx.exclusive_lock->block_requests(0);
1493
1494 if (request_lock) {
1495 m_image_ctx.exclusive_lock->acquire_lock(&ctx);
1496 } else {
1497 m_image_ctx.exclusive_lock->try_acquire_lock(&ctx);
1498 }
7c673cae
FG
1499 }
1500 }
1501
31f18b77 1502 int r = 0;
b32b8144 1503 if (attempting_lock) {
7c673cae
FG
1504 r = ctx.wait();
1505 }
b32b8144 1506
7c673cae 1507 m_image_ctx.owner_lock.get_read();
b32b8144
FG
1508 if (attempting_lock && m_image_ctx.exclusive_lock != nullptr) {
1509 m_image_ctx.exclusive_lock->unblock_requests();
1510 }
7c673cae 1511
b32b8144
FG
1512 if (r < 0) {
1513 return r;
1514 } else if (m_image_ctx.exclusive_lock != nullptr &&
1515 !m_image_ctx.exclusive_lock->is_lock_owner()) {
1516 return -EROFS;
1517 }
1518
1519 return 0;
7c673cae
FG
1520}
1521
1522template <typename I>
1523int Operations<I>::invoke_async_request(const std::string& request_type,
1524 bool permit_snapshot,
1525 const boost::function<void(Context*)>& local_request,
1526 const boost::function<void(Context*)>& remote_request) {
1527 C_SaferCond ctx;
1528 C_InvokeAsyncRequest<I> *req = new C_InvokeAsyncRequest<I>(m_image_ctx,
1529 request_type,
1530 permit_snapshot,
1531 local_request,
1532 remote_request,
1533 {}, &ctx);
1534 req->send();
1535 return ctx.wait();
1536}
1537
1538} // namespace librbd
1539
1540template class librbd::Operations<librbd::ImageCtx>;