1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
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/perf_counters.h"
9 #include "common/WorkQueue.h"
10 #include "osdc/Striper.h"
12 #include "librbd/ExclusiveLock.h"
13 #include "librbd/ImageCtx.h"
14 #include "librbd/ImageState.h"
15 #include "librbd/ImageWatcher.h"
16 #include "librbd/ObjectMap.h"
17 #include "librbd/Types.h"
18 #include "librbd/Utils.h"
19 #include "librbd/api/Config.h"
20 #include "librbd/journal/DisabledPolicy.h"
21 #include "librbd/journal/StandardPolicy.h"
22 #include "librbd/operation/DisableFeaturesRequest.h"
23 #include "librbd/operation/EnableFeaturesRequest.h"
24 #include "librbd/operation/FlattenRequest.h"
25 #include "librbd/operation/MetadataRemoveRequest.h"
26 #include "librbd/operation/MetadataSetRequest.h"
27 #include "librbd/operation/MigrateRequest.h"
28 #include "librbd/operation/ObjectMapIterate.h"
29 #include "librbd/operation/RebuildObjectMapRequest.h"
30 #include "librbd/operation/RenameRequest.h"
31 #include "librbd/operation/ResizeRequest.h"
32 #include "librbd/operation/SnapshotCreateRequest.h"
33 #include "librbd/operation/SnapshotProtectRequest.h"
34 #include "librbd/operation/SnapshotRemoveRequest.h"
35 #include "librbd/operation/SnapshotRenameRequest.h"
36 #include "librbd/operation/SnapshotRollbackRequest.h"
37 #include "librbd/operation/SnapshotUnprotectRequest.h"
38 #include "librbd/operation/SnapshotLimitRequest.h"
39 #include "librbd/operation/SparsifyRequest.h"
41 #include <boost/bind.hpp>
42 #include <boost/scope_exit.hpp>
44 #define dout_subsys ceph_subsys_rbd
46 #define dout_prefix *_dout << "librbd::Operations: "
53 struct C_NotifyUpdate
: public Context
{
56 bool notified
= false;
58 C_NotifyUpdate(I
&image_ctx
, Context
*on_finish
)
59 : image_ctx(image_ctx
), on_finish(on_finish
) {
62 void complete(int r
) override
{
63 CephContext
*cct
= image_ctx
.cct
;
65 if (r
== -ETIMEDOUT
) {
66 // don't fail the op if a peer fails to get the update notification
67 lderr(cct
) << "update notification timed-out" << dendl
;
69 } else if (r
== -ENOENT
) {
70 // don't fail if header is missing (e.g. v1 image rename)
71 ldout(cct
, 5) << "update notification on missing header" << dendl
;
74 lderr(cct
) << "update notification failed: " << cpp_strerror(r
)
82 // op failed -- no need to send update notification
88 image_ctx
.notify_update(this);
90 void finish(int r
) override
{
91 on_finish
->complete(r
);
96 struct C_InvokeAsyncRequest
: public Context
{
102 * . . . . . . | . . . . . . . . . . . . . . . . . .
105 * . REFRESH_IMAGE (skip if not needed) .
108 * . ACQUIRE_LOCK (skip if exclusive lock .
109 * . | disabled or has lock) .
111 * . /--------/ \--------\ . . . . . . . . . . . . .
114 * LOCAL_REQUEST REMOTE_REQUEST
117 * \--------\ /--------/
126 std::string request_type
;
127 bool permit_snapshot
;
128 boost::function
<void(Context
*)> local
;
129 boost::function
<void(Context
*)> remote
;
130 std::set
<int> filter_error_codes
;
132 bool request_lock
= false;
134 C_InvokeAsyncRequest(I
&image_ctx
, const std::string
& request_type
,
135 bool permit_snapshot
,
136 const boost::function
<void(Context
*)>& local
,
137 const boost::function
<void(Context
*)>& remote
,
138 const std::set
<int> &filter_error_codes
,
140 : image_ctx(image_ctx
), request_type(request_type
),
141 permit_snapshot(permit_snapshot
), local(local
), remote(remote
),
142 filter_error_codes(filter_error_codes
), on_finish(on_finish
) {
146 send_refresh_image();
149 void send_refresh_image() {
150 if (!image_ctx
.state
->is_refresh_required()) {
151 send_acquire_exclusive_lock();
155 CephContext
*cct
= image_ctx
.cct
;
156 ldout(cct
, 20) << __func__
<< dendl
;
158 Context
*ctx
= util::create_context_callback
<
159 C_InvokeAsyncRequest
<I
>,
160 &C_InvokeAsyncRequest
<I
>::handle_refresh_image
>(this);
161 image_ctx
.state
->refresh(ctx
);
164 void handle_refresh_image(int r
) {
165 CephContext
*cct
= image_ctx
.cct
;
166 ldout(cct
, 20) << __func__
<< ": r=" << r
<< dendl
;
169 lderr(cct
) << "failed to refresh image: " << cpp_strerror(r
) << dendl
;
174 send_acquire_exclusive_lock();
177 void send_acquire_exclusive_lock() {
178 // context can complete before owner_lock is unlocked
179 RWLock
&owner_lock(image_ctx
.owner_lock
);
180 owner_lock
.get_read();
181 image_ctx
.snap_lock
.get_read();
182 if (image_ctx
.read_only
||
183 (!permit_snapshot
&& image_ctx
.snap_id
!= CEPH_NOSNAP
)) {
184 image_ctx
.snap_lock
.put_read();
185 owner_lock
.put_read();
189 image_ctx
.snap_lock
.put_read();
191 if (image_ctx
.exclusive_lock
== nullptr) {
192 send_local_request();
193 owner_lock
.put_read();
195 } else if (image_ctx
.image_watcher
== nullptr) {
196 owner_lock
.put_read();
201 if (image_ctx
.exclusive_lock
->is_lock_owner() &&
202 image_ctx
.exclusive_lock
->accept_requests()) {
203 send_local_request();
204 owner_lock
.put_read();
208 CephContext
*cct
= image_ctx
.cct
;
209 ldout(cct
, 20) << __func__
<< dendl
;
211 Context
*ctx
= util::create_async_context_callback(
212 image_ctx
, util::create_context_callback
<
213 C_InvokeAsyncRequest
<I
>,
214 &C_InvokeAsyncRequest
<I
>::handle_acquire_exclusive_lock
>(this));
217 // current lock owner doesn't support op -- try to perform
218 // the action locally
219 request_lock
= false;
220 image_ctx
.exclusive_lock
->acquire_lock(ctx
);
222 image_ctx
.exclusive_lock
->try_acquire_lock(ctx
);
224 owner_lock
.put_read();
227 void handle_acquire_exclusive_lock(int r
) {
228 CephContext
*cct
= image_ctx
.cct
;
229 ldout(cct
, 20) << __func__
<< ": r=" << r
<< dendl
;
232 complete(r
== -EBLACKLISTED
? -EBLACKLISTED
: -EROFS
);
236 // context can complete before owner_lock is unlocked
237 RWLock
&owner_lock(image_ctx
.owner_lock
);
238 owner_lock
.get_read();
239 if (image_ctx
.exclusive_lock
== nullptr ||
240 image_ctx
.exclusive_lock
->is_lock_owner()) {
241 send_local_request();
242 owner_lock
.put_read();
246 send_remote_request();
247 owner_lock
.put_read();
250 void send_remote_request() {
251 ceph_assert(image_ctx
.owner_lock
.is_locked());
253 CephContext
*cct
= image_ctx
.cct
;
254 ldout(cct
, 20) << __func__
<< dendl
;
256 Context
*ctx
= util::create_async_context_callback(
257 image_ctx
, util::create_context_callback
<
258 C_InvokeAsyncRequest
<I
>,
259 &C_InvokeAsyncRequest
<I
>::handle_remote_request
>(this));
263 void handle_remote_request(int r
) {
264 CephContext
*cct
= image_ctx
.cct
;
265 ldout(cct
, 20) << __func__
<< ": r=" << r
<< dendl
;
267 if (r
== -EOPNOTSUPP
) {
268 ldout(cct
, 5) << request_type
<< " not supported by current lock owner"
271 send_refresh_image();
273 } else if (r
!= -ETIMEDOUT
&& r
!= -ERESTART
) {
274 image_ctx
.state
->handle_update_notification();
280 ldout(cct
, 5) << request_type
<< " timed out notifying lock owner"
282 send_refresh_image();
285 void send_local_request() {
286 ceph_assert(image_ctx
.owner_lock
.is_locked());
288 CephContext
*cct
= image_ctx
.cct
;
289 ldout(cct
, 20) << __func__
<< dendl
;
291 Context
*ctx
= util::create_async_context_callback(
292 image_ctx
, util::create_context_callback
<
293 C_InvokeAsyncRequest
<I
>,
294 &C_InvokeAsyncRequest
<I
>::handle_local_request
>(this));
298 void handle_local_request(int r
) {
299 CephContext
*cct
= image_ctx
.cct
;
300 ldout(cct
, 20) << __func__
<< ": r=" << r
<< dendl
;
302 if (r
== -ERESTART
) {
303 send_refresh_image();
309 void finish(int r
) override
{
310 if (filter_error_codes
.count(r
) != 0) {
313 on_finish
->complete(r
);
317 template <typename I
>
318 bool needs_invalidate(I
& image_ctx
, uint64_t object_no
,
319 uint8_t current_state
, uint8_t new_state
) {
320 if ( (current_state
== OBJECT_EXISTS
||
321 current_state
== OBJECT_EXISTS_CLEAN
) &&
322 (new_state
== OBJECT_NONEXISTENT
||
323 new_state
== OBJECT_PENDING
)) {
329 } // anonymous namespace
331 template <typename I
>
332 Operations
<I
>::Operations(I
&image_ctx
)
333 : m_image_ctx(image_ctx
), m_async_request_seq(0) {
336 template <typename I
>
337 int Operations
<I
>::flatten(ProgressContext
&prog_ctx
) {
338 CephContext
*cct
= m_image_ctx
.cct
;
339 ldout(cct
, 20) << "flatten" << dendl
;
341 int r
= m_image_ctx
.state
->refresh_if_required();
346 if (m_image_ctx
.read_only
) {
351 RWLock::RLocker
parent_locker(m_image_ctx
.parent_lock
);
352 if (m_image_ctx
.parent_md
.spec
.pool_id
== -1) {
353 lderr(cct
) << "image has no parent" << dendl
;
358 uint64_t request_id
= ++m_async_request_seq
;
359 r
= invoke_async_request("flatten", false,
360 boost::bind(&Operations
<I
>::execute_flatten
, this,
361 boost::ref(prog_ctx
), _1
),
362 boost::bind(&ImageWatcher
<I
>::notify_flatten
,
363 m_image_ctx
.image_watcher
, request_id
,
364 boost::ref(prog_ctx
), _1
));
366 if (r
< 0 && r
!= -EINVAL
) {
369 ldout(cct
, 20) << "flatten finished" << dendl
;
373 template <typename I
>
374 void Operations
<I
>::execute_flatten(ProgressContext
&prog_ctx
,
375 Context
*on_finish
) {
376 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
377 ceph_assert(m_image_ctx
.exclusive_lock
== nullptr ||
378 m_image_ctx
.exclusive_lock
->is_lock_owner());
380 CephContext
*cct
= m_image_ctx
.cct
;
381 ldout(cct
, 20) << "flatten" << dendl
;
383 if (m_image_ctx
.read_only
|| m_image_ctx
.operations_disabled
) {
384 on_finish
->complete(-EROFS
);
388 m_image_ctx
.snap_lock
.get_read();
389 m_image_ctx
.parent_lock
.get_read();
391 // can't flatten a non-clone
392 if (m_image_ctx
.parent_md
.spec
.pool_id
== -1) {
393 lderr(cct
) << "image has no parent" << dendl
;
394 m_image_ctx
.parent_lock
.put_read();
395 m_image_ctx
.snap_lock
.put_read();
396 on_finish
->complete(-EINVAL
);
399 if (m_image_ctx
.snap_id
!= CEPH_NOSNAP
) {
400 lderr(cct
) << "snapshots cannot be flattened" << dendl
;
401 m_image_ctx
.parent_lock
.put_read();
402 m_image_ctx
.snap_lock
.put_read();
403 on_finish
->complete(-EROFS
);
407 ::SnapContext snapc
= m_image_ctx
.snapc
;
410 int r
= m_image_ctx
.get_parent_overlap(CEPH_NOSNAP
, &overlap
);
412 ceph_assert(overlap
<= m_image_ctx
.size
);
414 uint64_t overlap_objects
= Striper::get_num_objects(m_image_ctx
.layout
,
417 m_image_ctx
.parent_lock
.put_read();
418 m_image_ctx
.snap_lock
.put_read();
420 operation::FlattenRequest
<I
> *req
= new operation::FlattenRequest
<I
>(
421 m_image_ctx
, new C_NotifyUpdate
<I
>(m_image_ctx
, on_finish
), overlap_objects
,
426 template <typename I
>
427 int Operations
<I
>::rebuild_object_map(ProgressContext
&prog_ctx
) {
428 CephContext
*cct
= m_image_ctx
.cct
;
429 ldout(cct
, 10) << "rebuild_object_map" << dendl
;
431 int r
= m_image_ctx
.state
->refresh_if_required();
436 uint64_t request_id
= ++m_async_request_seq
;
437 r
= invoke_async_request("rebuild object map", true,
438 boost::bind(&Operations
<I
>::execute_rebuild_object_map
,
439 this, boost::ref(prog_ctx
), _1
),
440 boost::bind(&ImageWatcher
<I
>::notify_rebuild_object_map
,
441 m_image_ctx
.image_watcher
, request_id
,
442 boost::ref(prog_ctx
), _1
));
444 ldout(cct
, 10) << "rebuild object map finished" << dendl
;
451 template <typename I
>
452 void Operations
<I
>::execute_rebuild_object_map(ProgressContext
&prog_ctx
,
453 Context
*on_finish
) {
454 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
455 ceph_assert(m_image_ctx
.exclusive_lock
== nullptr ||
456 m_image_ctx
.exclusive_lock
->is_lock_owner());
458 CephContext
*cct
= m_image_ctx
.cct
;
459 ldout(cct
, 5) << this << " " << __func__
<< dendl
;
461 if (m_image_ctx
.read_only
|| m_image_ctx
.operations_disabled
) {
462 on_finish
->complete(-EROFS
);
466 if (!m_image_ctx
.test_features(RBD_FEATURE_OBJECT_MAP
)) {
467 lderr(cct
) << "image must support object-map feature" << dendl
;
468 on_finish
->complete(-EINVAL
);
472 operation::RebuildObjectMapRequest
<I
> *req
=
473 new operation::RebuildObjectMapRequest
<I
>(
474 m_image_ctx
, new C_NotifyUpdate
<I
>(m_image_ctx
, on_finish
), prog_ctx
);
478 template <typename I
>
479 int Operations
<I
>::check_object_map(ProgressContext
&prog_ctx
) {
480 CephContext
*cct
= m_image_ctx
.cct
;
481 ldout(cct
, 5) << this << " " << __func__
<< dendl
;
482 int r
= m_image_ctx
.state
->refresh_if_required();
487 r
= invoke_async_request("check object map", true,
488 boost::bind(&Operations
<I
>::check_object_map
, this,
489 boost::ref(prog_ctx
), _1
),
491 m_image_ctx
.op_work_queue
->queue(c
, -EOPNOTSUPP
);
497 template <typename I
>
498 void Operations
<I
>::object_map_iterate(ProgressContext
&prog_ctx
,
499 operation::ObjectIterateWork
<I
> handle_mismatch
,
500 Context
*on_finish
) {
501 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
502 ceph_assert(m_image_ctx
.exclusive_lock
== nullptr ||
503 m_image_ctx
.exclusive_lock
->is_lock_owner());
505 if (!m_image_ctx
.test_features(RBD_FEATURE_OBJECT_MAP
)) {
506 on_finish
->complete(-EINVAL
);
510 operation::ObjectMapIterateRequest
<I
> *req
=
511 new operation::ObjectMapIterateRequest
<I
>(m_image_ctx
, on_finish
,
512 prog_ctx
, handle_mismatch
);
516 template <typename I
>
517 void Operations
<I
>::check_object_map(ProgressContext
&prog_ctx
,
518 Context
*on_finish
) {
519 object_map_iterate(prog_ctx
, needs_invalidate
, on_finish
);
522 template <typename I
>
523 int Operations
<I
>::rename(const char *dstname
) {
524 CephContext
*cct
= m_image_ctx
.cct
;
525 ldout(cct
, 5) << this << " " << __func__
<< ": dest_name=" << dstname
528 int r
= librbd::detect_format(m_image_ctx
.md_ctx
, dstname
, NULL
, NULL
);
529 if (r
< 0 && r
!= -ENOENT
) {
530 lderr(cct
) << "error checking for existing image called "
531 << dstname
<< ":" << cpp_strerror(r
) << dendl
;
535 lderr(cct
) << "rbd image " << dstname
<< " already exists" << dendl
;
539 if (m_image_ctx
.test_features(RBD_FEATURE_JOURNALING
)) {
540 r
= invoke_async_request("rename", true,
541 boost::bind(&Operations
<I
>::execute_rename
, this,
543 boost::bind(&ImageWatcher
<I
>::notify_rename
,
544 m_image_ctx
.image_watcher
, dstname
,
546 if (r
< 0 && r
!= -EEXIST
) {
550 C_SaferCond cond_ctx
;
552 RWLock::RLocker
owner_lock(m_image_ctx
.owner_lock
);
553 execute_rename(dstname
, &cond_ctx
);
562 m_image_ctx
.set_image_name(dstname
);
566 template <typename I
>
567 void Operations
<I
>::execute_rename(const std::string
&dest_name
,
568 Context
*on_finish
) {
569 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
570 if (m_image_ctx
.test_features(RBD_FEATURE_JOURNALING
)) {
571 ceph_assert(m_image_ctx
.exclusive_lock
== nullptr ||
572 m_image_ctx
.exclusive_lock
->is_lock_owner());
575 if (m_image_ctx
.operations_disabled
) {
576 on_finish
->complete(-EROFS
);
580 m_image_ctx
.snap_lock
.get_read();
581 if (m_image_ctx
.name
== dest_name
) {
582 m_image_ctx
.snap_lock
.put_read();
583 on_finish
->complete(-EEXIST
);
586 m_image_ctx
.snap_lock
.put_read();
588 CephContext
*cct
= m_image_ctx
.cct
;
589 ldout(cct
, 5) << this << " " << __func__
<< ": dest_name=" << dest_name
592 if (m_image_ctx
.old_format
) {
593 // unregister watch before and register back after rename
594 on_finish
= new C_NotifyUpdate
<I
>(m_image_ctx
, on_finish
);
595 on_finish
= new FunctionContext([this, on_finish
](int r
) {
596 if (m_image_ctx
.old_format
) {
597 m_image_ctx
.image_watcher
->set_oid(m_image_ctx
.header_oid
);
599 m_image_ctx
.image_watcher
->register_watch(on_finish
);
601 on_finish
= new FunctionContext([this, dest_name
, on_finish
](int r
) {
602 RWLock::RLocker
owner_locker(m_image_ctx
.owner_lock
);
603 operation::RenameRequest
<I
> *req
= new operation::RenameRequest
<I
>(
604 m_image_ctx
, on_finish
, dest_name
);
607 m_image_ctx
.image_watcher
->unregister_watch(on_finish
);
610 operation::RenameRequest
<I
> *req
= new operation::RenameRequest
<I
>(
611 m_image_ctx
, on_finish
, dest_name
);
615 template <typename I
>
616 int Operations
<I
>::resize(uint64_t size
, bool allow_shrink
, ProgressContext
& prog_ctx
) {
617 CephContext
*cct
= m_image_ctx
.cct
;
619 m_image_ctx
.snap_lock
.get_read();
620 ldout(cct
, 5) << this << " " << __func__
<< ": "
621 << "size=" << m_image_ctx
.size
<< ", "
622 << "new_size=" << size
<< dendl
;
623 m_image_ctx
.snap_lock
.put_read();
625 int r
= m_image_ctx
.state
->refresh_if_required();
630 if (m_image_ctx
.test_features(RBD_FEATURE_OBJECT_MAP
) &&
631 !ObjectMap
<>::is_compatible(m_image_ctx
.layout
, size
)) {
632 lderr(cct
) << "New size not compatible with object map" << dendl
;
636 uint64_t request_id
= ++m_async_request_seq
;
637 r
= invoke_async_request("resize", false,
638 boost::bind(&Operations
<I
>::execute_resize
, this,
639 size
, allow_shrink
, boost::ref(prog_ctx
), _1
, 0),
640 boost::bind(&ImageWatcher
<I
>::notify_resize
,
641 m_image_ctx
.image_watcher
, request_id
,
642 size
, allow_shrink
, boost::ref(prog_ctx
), _1
));
644 m_image_ctx
.perfcounter
->inc(l_librbd_resize
);
645 ldout(cct
, 2) << "resize finished" << dendl
;
649 template <typename I
>
650 void Operations
<I
>::execute_resize(uint64_t size
, bool allow_shrink
, ProgressContext
&prog_ctx
,
652 uint64_t journal_op_tid
) {
653 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
654 ceph_assert(m_image_ctx
.exclusive_lock
== nullptr ||
655 m_image_ctx
.exclusive_lock
->is_lock_owner());
657 CephContext
*cct
= m_image_ctx
.cct
;
658 m_image_ctx
.snap_lock
.get_read();
659 ldout(cct
, 5) << this << " " << __func__
<< ": "
660 << "size=" << m_image_ctx
.size
<< ", "
661 << "new_size=" << size
<< dendl
;
663 if (m_image_ctx
.snap_id
!= CEPH_NOSNAP
|| m_image_ctx
.read_only
||
664 m_image_ctx
.operations_disabled
) {
665 m_image_ctx
.snap_lock
.put_read();
666 on_finish
->complete(-EROFS
);
668 } else if (m_image_ctx
.test_features(RBD_FEATURE_OBJECT_MAP
,
669 m_image_ctx
.snap_lock
) &&
670 !ObjectMap
<>::is_compatible(m_image_ctx
.layout
, size
)) {
671 m_image_ctx
.snap_lock
.put_read();
672 on_finish
->complete(-EINVAL
);
675 m_image_ctx
.snap_lock
.put_read();
677 operation::ResizeRequest
<I
> *req
= new operation::ResizeRequest
<I
>(
678 m_image_ctx
, new C_NotifyUpdate
<I
>(m_image_ctx
, on_finish
), size
, allow_shrink
,
679 prog_ctx
, journal_op_tid
, false);
683 template <typename I
>
684 int Operations
<I
>::snap_create(const cls::rbd::SnapshotNamespace
&snap_namespace
,
685 const std::string
& snap_name
) {
686 if (m_image_ctx
.read_only
) {
690 int r
= m_image_ctx
.state
->refresh_if_required();
696 snap_create(snap_namespace
, snap_name
, &ctx
);
703 m_image_ctx
.perfcounter
->inc(l_librbd_snap_create
);
707 template <typename I
>
708 void Operations
<I
>::snap_create(const cls::rbd::SnapshotNamespace
&snap_namespace
,
709 const std::string
& snap_name
,
710 Context
*on_finish
) {
711 CephContext
*cct
= m_image_ctx
.cct
;
712 ldout(cct
, 5) << this << " " << __func__
<< ": snap_name=" << snap_name
715 if (m_image_ctx
.read_only
) {
716 on_finish
->complete(-EROFS
);
720 m_image_ctx
.snap_lock
.get_read();
721 if (m_image_ctx
.get_snap_id(snap_namespace
, snap_name
) != CEPH_NOSNAP
) {
722 m_image_ctx
.snap_lock
.put_read();
723 on_finish
->complete(-EEXIST
);
726 m_image_ctx
.snap_lock
.put_read();
728 C_InvokeAsyncRequest
<I
> *req
= new C_InvokeAsyncRequest
<I
>(
729 m_image_ctx
, "snap_create", true,
730 boost::bind(&Operations
<I
>::execute_snap_create
, this, snap_namespace
, snap_name
,
732 boost::bind(&ImageWatcher
<I
>::notify_snap_create
, m_image_ctx
.image_watcher
,
733 snap_namespace
, snap_name
, _1
),
734 {-EEXIST
}, on_finish
);
738 template <typename I
>
739 void Operations
<I
>::execute_snap_create(const cls::rbd::SnapshotNamespace
&snap_namespace
,
740 const std::string
&snap_name
,
742 uint64_t journal_op_tid
,
743 bool skip_object_map
) {
744 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
745 ceph_assert(m_image_ctx
.exclusive_lock
== nullptr ||
746 m_image_ctx
.exclusive_lock
->is_lock_owner());
748 CephContext
*cct
= m_image_ctx
.cct
;
749 ldout(cct
, 5) << this << " " << __func__
<< ": snap_name=" << snap_name
752 if (m_image_ctx
.operations_disabled
) {
753 on_finish
->complete(-EROFS
);
757 m_image_ctx
.snap_lock
.get_read();
758 if (m_image_ctx
.get_snap_id(snap_namespace
, snap_name
) != CEPH_NOSNAP
) {
759 m_image_ctx
.snap_lock
.put_read();
760 on_finish
->complete(-EEXIST
);
763 m_image_ctx
.snap_lock
.put_read();
765 operation::SnapshotCreateRequest
<I
> *req
=
766 new operation::SnapshotCreateRequest
<I
>(
767 m_image_ctx
, new C_NotifyUpdate
<I
>(m_image_ctx
, on_finish
),
768 snap_namespace
, snap_name
, journal_op_tid
, skip_object_map
);
772 template <typename I
>
773 int Operations
<I
>::snap_rollback(const cls::rbd::SnapshotNamespace
& snap_namespace
,
774 const std::string
& snap_name
,
775 ProgressContext
& prog_ctx
) {
776 CephContext
*cct
= m_image_ctx
.cct
;
777 ldout(cct
, 5) << this << " " << __func__
<< ": snap_name=" << snap_name
780 int r
= m_image_ctx
.state
->refresh_if_required();
784 C_SaferCond cond_ctx
;
786 RWLock::RLocker
owner_locker(m_image_ctx
.owner_lock
);
788 // need to drop snap_lock before invalidating cache
789 RWLock::RLocker
snap_locker(m_image_ctx
.snap_lock
);
790 if (!m_image_ctx
.snap_exists
) {
794 if (m_image_ctx
.snap_id
!= CEPH_NOSNAP
|| m_image_ctx
.read_only
) {
798 uint64_t snap_id
= m_image_ctx
.get_snap_id(snap_namespace
, snap_name
);
799 if (snap_id
== CEPH_NOSNAP
) {
800 lderr(cct
) << "No such snapshot found." << dendl
;
805 r
= prepare_image_update(false);
810 execute_snap_rollback(snap_namespace
, snap_name
, prog_ctx
, &cond_ctx
);
818 m_image_ctx
.perfcounter
->inc(l_librbd_snap_rollback
);
822 template <typename I
>
823 void Operations
<I
>::execute_snap_rollback(const cls::rbd::SnapshotNamespace
& snap_namespace
,
824 const std::string
&snap_name
,
825 ProgressContext
& prog_ctx
,
826 Context
*on_finish
) {
827 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
828 CephContext
*cct
= m_image_ctx
.cct
;
829 ldout(cct
, 5) << this << " " << __func__
<< ": snap_name=" << snap_name
832 if (m_image_ctx
.operations_disabled
) {
833 on_finish
->complete(-EROFS
);
837 m_image_ctx
.snap_lock
.get_read();
838 uint64_t snap_id
= m_image_ctx
.get_snap_id(snap_namespace
, snap_name
);
839 if (snap_id
== CEPH_NOSNAP
) {
840 lderr(cct
) << "No such snapshot found." << dendl
;
841 m_image_ctx
.snap_lock
.put_read();
842 on_finish
->complete(-ENOENT
);
846 uint64_t new_size
= m_image_ctx
.get_image_size(snap_id
);
847 m_image_ctx
.snap_lock
.put_read();
849 // async mode used for journal replay
850 operation::SnapshotRollbackRequest
<I
> *request
=
851 new operation::SnapshotRollbackRequest
<I
>(
852 m_image_ctx
, new C_NotifyUpdate
<I
>(m_image_ctx
, on_finish
), snap_namespace
, snap_name
,
853 snap_id
, new_size
, prog_ctx
);
857 template <typename I
>
858 int Operations
<I
>::snap_remove(const cls::rbd::SnapshotNamespace
& snap_namespace
,
859 const std::string
& snap_name
) {
860 if (m_image_ctx
.read_only
) {
864 int r
= m_image_ctx
.state
->refresh_if_required();
870 snap_remove(snap_namespace
, snap_name
, &ctx
);
877 m_image_ctx
.perfcounter
->inc(l_librbd_snap_remove
);
881 template <typename I
>
882 void Operations
<I
>::snap_remove(const cls::rbd::SnapshotNamespace
& snap_namespace
,
883 const std::string
& snap_name
,
884 Context
*on_finish
) {
885 CephContext
*cct
= m_image_ctx
.cct
;
886 ldout(cct
, 5) << this << " " << __func__
<< ": snap_name=" << snap_name
889 if (m_image_ctx
.read_only
) {
890 on_finish
->complete(-EROFS
);
894 // quickly filter out duplicate ops
895 m_image_ctx
.snap_lock
.get_read();
896 if (m_image_ctx
.get_snap_id(snap_namespace
, snap_name
) == CEPH_NOSNAP
) {
897 m_image_ctx
.snap_lock
.put_read();
898 on_finish
->complete(-ENOENT
);
902 bool proxy_op
= ((m_image_ctx
.features
& RBD_FEATURE_FAST_DIFF
) != 0 ||
903 (m_image_ctx
.features
& RBD_FEATURE_JOURNALING
) != 0);
904 m_image_ctx
.snap_lock
.put_read();
907 C_InvokeAsyncRequest
<I
> *req
= new C_InvokeAsyncRequest
<I
>(
908 m_image_ctx
, "snap_remove", true,
909 boost::bind(&Operations
<I
>::execute_snap_remove
, this, snap_namespace
, snap_name
, _1
),
910 boost::bind(&ImageWatcher
<I
>::notify_snap_remove
, m_image_ctx
.image_watcher
,
911 snap_namespace
, snap_name
, _1
),
912 {-ENOENT
}, on_finish
);
915 RWLock::RLocker
owner_lock(m_image_ctx
.owner_lock
);
916 execute_snap_remove(snap_namespace
, snap_name
, on_finish
);
920 template <typename I
>
921 void Operations
<I
>::execute_snap_remove(const cls::rbd::SnapshotNamespace
& snap_namespace
,
922 const std::string
&snap_name
,
923 Context
*on_finish
) {
924 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
926 if ((m_image_ctx
.features
& RBD_FEATURE_FAST_DIFF
) != 0) {
927 ceph_assert(m_image_ctx
.exclusive_lock
== nullptr ||
928 m_image_ctx
.exclusive_lock
->is_lock_owner());
932 CephContext
*cct
= m_image_ctx
.cct
;
933 ldout(cct
, 5) << this << " " << __func__
<< ": snap_name=" << snap_name
936 if (m_image_ctx
.operations_disabled
) {
937 on_finish
->complete(-EROFS
);
941 m_image_ctx
.snap_lock
.get_read();
942 uint64_t snap_id
= m_image_ctx
.get_snap_id(snap_namespace
, snap_name
);
943 if (snap_id
== CEPH_NOSNAP
) {
944 lderr(m_image_ctx
.cct
) << "No such snapshot found." << dendl
;
945 m_image_ctx
.snap_lock
.put_read();
946 on_finish
->complete(-ENOENT
);
951 int r
= m_image_ctx
.is_snap_protected(snap_id
, &is_protected
);
953 m_image_ctx
.snap_lock
.put_read();
954 on_finish
->complete(r
);
956 } else if (is_protected
) {
957 lderr(m_image_ctx
.cct
) << "snapshot is protected" << dendl
;
958 m_image_ctx
.snap_lock
.put_read();
959 on_finish
->complete(-EBUSY
);
962 m_image_ctx
.snap_lock
.put_read();
964 operation::SnapshotRemoveRequest
<I
> *req
=
965 new operation::SnapshotRemoveRequest
<I
>(
966 m_image_ctx
, new C_NotifyUpdate
<I
>(m_image_ctx
, on_finish
),
967 snap_namespace
, snap_name
, snap_id
);
971 template <typename I
>
972 int Operations
<I
>::snap_rename(const char *srcname
, const char *dstname
) {
973 CephContext
*cct
= m_image_ctx
.cct
;
974 ldout(cct
, 5) << this << " " << __func__
<< ": "
975 << "snap_name=" << srcname
<< ", "
976 << "new_snap_name=" << dstname
<< dendl
;
979 if (m_image_ctx
.read_only
) {
983 int r
= m_image_ctx
.state
->refresh_if_required();
988 RWLock::RLocker
l(m_image_ctx
.snap_lock
);
989 snap_id
= m_image_ctx
.get_snap_id(cls::rbd::UserSnapshotNamespace(), srcname
);
990 if (snap_id
== CEPH_NOSNAP
) {
993 if (m_image_ctx
.get_snap_id(cls::rbd::UserSnapshotNamespace(), dstname
) != CEPH_NOSNAP
) {
998 if (m_image_ctx
.test_features(RBD_FEATURE_JOURNALING
)) {
999 r
= invoke_async_request("snap_rename", true,
1000 boost::bind(&Operations
<I
>::execute_snap_rename
,
1001 this, snap_id
, dstname
, _1
),
1002 boost::bind(&ImageWatcher
<I
>::notify_snap_rename
,
1003 m_image_ctx
.image_watcher
, snap_id
,
1005 if (r
< 0 && r
!= -EEXIST
) {
1009 C_SaferCond cond_ctx
;
1011 RWLock::RLocker
owner_lock(m_image_ctx
.owner_lock
);
1012 execute_snap_rename(snap_id
, dstname
, &cond_ctx
);
1015 r
= cond_ctx
.wait();
1021 m_image_ctx
.perfcounter
->inc(l_librbd_snap_rename
);
1025 template <typename I
>
1026 void Operations
<I
>::execute_snap_rename(const uint64_t src_snap_id
,
1027 const std::string
&dest_snap_name
,
1028 Context
*on_finish
) {
1029 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
1030 if ((m_image_ctx
.features
& RBD_FEATURE_JOURNALING
) != 0) {
1031 ceph_assert(m_image_ctx
.exclusive_lock
== nullptr ||
1032 m_image_ctx
.exclusive_lock
->is_lock_owner());
1035 if (m_image_ctx
.operations_disabled
) {
1036 on_finish
->complete(-EROFS
);
1040 m_image_ctx
.snap_lock
.get_read();
1041 if (m_image_ctx
.get_snap_id(cls::rbd::UserSnapshotNamespace(),
1042 dest_snap_name
) != CEPH_NOSNAP
) {
1043 // Renaming is supported for snapshots from user namespace only.
1044 m_image_ctx
.snap_lock
.put_read();
1045 on_finish
->complete(-EEXIST
);
1048 m_image_ctx
.snap_lock
.put_read();
1050 CephContext
*cct
= m_image_ctx
.cct
;
1051 ldout(cct
, 5) << this << " " << __func__
<< ": "
1052 << "snap_id=" << src_snap_id
<< ", "
1053 << "new_snap_name=" << dest_snap_name
<< dendl
;
1055 operation::SnapshotRenameRequest
<I
> *req
=
1056 new operation::SnapshotRenameRequest
<I
>(
1057 m_image_ctx
, new C_NotifyUpdate
<I
>(m_image_ctx
, on_finish
), src_snap_id
,
1062 template <typename I
>
1063 int Operations
<I
>::snap_protect(const cls::rbd::SnapshotNamespace
& snap_namespace
,
1064 const std::string
& snap_name
) {
1065 CephContext
*cct
= m_image_ctx
.cct
;
1066 ldout(cct
, 5) << this << " " << __func__
<< ": snap_name=" << snap_name
1069 if (m_image_ctx
.read_only
) {
1073 if (!m_image_ctx
.test_features(RBD_FEATURE_LAYERING
)) {
1074 lderr(cct
) << "image must support layering" << dendl
;
1078 int r
= m_image_ctx
.state
->refresh_if_required();
1084 RWLock::RLocker
snap_locker(m_image_ctx
.snap_lock
);
1086 r
= m_image_ctx
.is_snap_protected(m_image_ctx
.get_snap_id(snap_namespace
, snap_name
),
1097 if (m_image_ctx
.test_features(RBD_FEATURE_JOURNALING
)) {
1098 r
= invoke_async_request("snap_protect", true,
1099 boost::bind(&Operations
<I
>::execute_snap_protect
,
1100 this, snap_namespace
, snap_name
, _1
),
1101 boost::bind(&ImageWatcher
<I
>::notify_snap_protect
,
1102 m_image_ctx
.image_watcher
,
1103 snap_namespace
, snap_name
, _1
));
1104 if (r
< 0 && r
!= -EBUSY
) {
1108 C_SaferCond cond_ctx
;
1110 RWLock::RLocker
owner_lock(m_image_ctx
.owner_lock
);
1111 execute_snap_protect(snap_namespace
, snap_name
, &cond_ctx
);
1114 r
= cond_ctx
.wait();
1122 template <typename I
>
1123 void Operations
<I
>::execute_snap_protect(const cls::rbd::SnapshotNamespace
& snap_namespace
,
1124 const std::string
&snap_name
,
1125 Context
*on_finish
) {
1126 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
1127 if (m_image_ctx
.test_features(RBD_FEATURE_JOURNALING
)) {
1128 ceph_assert(m_image_ctx
.exclusive_lock
== nullptr ||
1129 m_image_ctx
.exclusive_lock
->is_lock_owner());
1132 if (m_image_ctx
.operations_disabled
) {
1133 on_finish
->complete(-EROFS
);
1137 m_image_ctx
.snap_lock
.get_read();
1139 int r
= m_image_ctx
.is_snap_protected(m_image_ctx
.get_snap_id(snap_namespace
, snap_name
),
1142 m_image_ctx
.snap_lock
.put_read();
1143 on_finish
->complete(r
);
1145 } else if (is_protected
) {
1146 m_image_ctx
.snap_lock
.put_read();
1147 on_finish
->complete(-EBUSY
);
1150 m_image_ctx
.snap_lock
.put_read();
1152 CephContext
*cct
= m_image_ctx
.cct
;
1153 ldout(cct
, 5) << this << " " << __func__
<< ": snap_name=" << snap_name
1156 operation::SnapshotProtectRequest
<I
> *request
=
1157 new operation::SnapshotProtectRequest
<I
>(
1158 m_image_ctx
, new C_NotifyUpdate
<I
>(m_image_ctx
, on_finish
), snap_namespace
, snap_name
);
1162 template <typename I
>
1163 int Operations
<I
>::snap_unprotect(const cls::rbd::SnapshotNamespace
& snap_namespace
,
1164 const std::string
& snap_name
) {
1165 CephContext
*cct
= m_image_ctx
.cct
;
1166 ldout(cct
, 5) << this << " " << __func__
<< ": snap_name=" << snap_name
1169 if (m_image_ctx
.read_only
) {
1173 int r
= m_image_ctx
.state
->refresh_if_required();
1179 RWLock::RLocker
snap_locker(m_image_ctx
.snap_lock
);
1180 bool is_unprotected
;
1181 r
= m_image_ctx
.is_snap_unprotected(m_image_ctx
.get_snap_id(snap_namespace
, snap_name
),
1187 if (is_unprotected
) {
1192 if (m_image_ctx
.test_features(RBD_FEATURE_JOURNALING
)) {
1193 r
= invoke_async_request("snap_unprotect", true,
1194 boost::bind(&Operations
<I
>::execute_snap_unprotect
,
1195 this, snap_namespace
, snap_name
, _1
),
1196 boost::bind(&ImageWatcher
<I
>::notify_snap_unprotect
,
1197 m_image_ctx
.image_watcher
,
1198 snap_namespace
, snap_name
, _1
));
1199 if (r
< 0 && r
!= -EINVAL
) {
1203 C_SaferCond cond_ctx
;
1205 RWLock::RLocker
owner_lock(m_image_ctx
.owner_lock
);
1206 execute_snap_unprotect(snap_namespace
, snap_name
, &cond_ctx
);
1209 r
= cond_ctx
.wait();
1217 template <typename I
>
1218 void Operations
<I
>::execute_snap_unprotect(const cls::rbd::SnapshotNamespace
& snap_namespace
,
1219 const std::string
&snap_name
,
1220 Context
*on_finish
) {
1221 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
1222 if (m_image_ctx
.test_features(RBD_FEATURE_JOURNALING
)) {
1223 ceph_assert(m_image_ctx
.exclusive_lock
== nullptr ||
1224 m_image_ctx
.exclusive_lock
->is_lock_owner());
1227 if (m_image_ctx
.operations_disabled
) {
1228 on_finish
->complete(-EROFS
);
1232 m_image_ctx
.snap_lock
.get_read();
1233 bool is_unprotected
;
1234 int r
= m_image_ctx
.is_snap_unprotected(m_image_ctx
.get_snap_id(snap_namespace
, snap_name
),
1237 m_image_ctx
.snap_lock
.put_read();
1238 on_finish
->complete(r
);
1240 } else if (is_unprotected
) {
1241 m_image_ctx
.snap_lock
.put_read();
1242 on_finish
->complete(-EINVAL
);
1245 m_image_ctx
.snap_lock
.put_read();
1247 CephContext
*cct
= m_image_ctx
.cct
;
1248 ldout(cct
, 5) << this << " " << __func__
<< ": snap_name=" << snap_name
1251 operation::SnapshotUnprotectRequest
<I
> *request
=
1252 new operation::SnapshotUnprotectRequest
<I
>(
1253 m_image_ctx
, new C_NotifyUpdate
<I
>(m_image_ctx
, on_finish
), snap_namespace
, snap_name
);
1257 template <typename I
>
1258 int Operations
<I
>::snap_set_limit(uint64_t limit
) {
1259 CephContext
*cct
= m_image_ctx
.cct
;
1260 ldout(cct
, 5) << this << " " << __func__
<< ": limit=" << limit
<< dendl
;
1262 if (m_image_ctx
.read_only
) {
1266 int r
= m_image_ctx
.state
->refresh_if_required();
1271 C_SaferCond limit_ctx
;
1273 RWLock::RLocker
owner_lock(m_image_ctx
.owner_lock
);
1274 r
= prepare_image_update(true);
1279 execute_snap_set_limit(limit
, &limit_ctx
);
1282 r
= limit_ctx
.wait();
1286 template <typename I
>
1287 void Operations
<I
>::execute_snap_set_limit(const uint64_t limit
,
1288 Context
*on_finish
) {
1289 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
1291 CephContext
*cct
= m_image_ctx
.cct
;
1292 ldout(cct
, 5) << this << " " << __func__
<< ": limit=" << limit
1295 operation::SnapshotLimitRequest
<I
> *request
=
1296 new operation::SnapshotLimitRequest
<I
>(m_image_ctx
, on_finish
, limit
);
1300 template <typename I
>
1301 int Operations
<I
>::update_features(uint64_t features
, bool enabled
) {
1302 CephContext
*cct
= m_image_ctx
.cct
;
1303 ldout(cct
, 5) << this << " " << __func__
<< ": features=" << features
1304 << ", enabled=" << enabled
<< dendl
;
1306 int r
= m_image_ctx
.state
->refresh_if_required();
1311 if (m_image_ctx
.read_only
) {
1313 } else if (m_image_ctx
.old_format
) {
1314 lderr(cct
) << "old-format images do not support features" << dendl
;
1318 uint64_t disable_mask
= (RBD_FEATURES_MUTABLE
|
1319 RBD_FEATURES_DISABLE_ONLY
);
1320 if ((enabled
&& (features
& RBD_FEATURES_MUTABLE
) != features
) ||
1321 (!enabled
&& (features
& disable_mask
) != features
)) {
1322 lderr(cct
) << "cannot update immutable features" << dendl
;
1326 bool set_object_map
= (features
& RBD_FEATURE_OBJECT_MAP
) == RBD_FEATURE_OBJECT_MAP
;
1327 bool set_fast_diff
= (features
& RBD_FEATURE_FAST_DIFF
) == RBD_FEATURE_FAST_DIFF
;
1328 bool exist_fast_diff
= (m_image_ctx
.features
& RBD_FEATURE_FAST_DIFF
) != 0;
1329 bool exist_object_map
= (m_image_ctx
.features
& RBD_FEATURE_OBJECT_MAP
) != 0;
1331 if ((enabled
&& ((set_object_map
&& !exist_fast_diff
) || (set_fast_diff
&& !exist_object_map
)))
1332 || (!enabled
&& (set_object_map
&& exist_fast_diff
))) {
1333 features
|= (RBD_FEATURE_OBJECT_MAP
| RBD_FEATURE_FAST_DIFF
);
1336 if (features
== 0) {
1337 lderr(cct
) << "update requires at least one feature" << dendl
;
1341 RWLock::RLocker
snap_locker(m_image_ctx
.snap_lock
);
1342 if (enabled
&& (features
& m_image_ctx
.features
) != 0) {
1343 lderr(cct
) << "one or more requested features are already enabled"
1347 if (!enabled
&& (features
& ~m_image_ctx
.features
) != 0) {
1348 lderr(cct
) << "one or more requested features are already disabled"
1354 // if disabling journaling, avoid attempting to open the journal
1355 // when acquiring the exclusive lock in case the journal is corrupt
1356 bool disabling_journal
= false;
1357 if (!enabled
&& ((features
& RBD_FEATURE_JOURNALING
) != 0)) {
1358 RWLock::WLocker
snap_locker(m_image_ctx
.snap_lock
);
1359 m_image_ctx
.set_journal_policy(new journal::DisabledPolicy());
1360 disabling_journal
= true;
1362 BOOST_SCOPE_EXIT_ALL( (this)(disabling_journal
) ) {
1363 if (disabling_journal
) {
1364 RWLock::WLocker
snap_locker(m_image_ctx
.snap_lock
);
1365 m_image_ctx
.set_journal_policy(
1366 new journal::StandardPolicy
<I
>(&m_image_ctx
));
1370 // The journal options are not passed to the lock owner in the
1371 // update features request. Therefore, if journaling is being
1372 // enabled, the lock should be locally acquired instead of
1373 // attempting to send the request to the peer.
1374 if (enabled
&& (features
& RBD_FEATURE_JOURNALING
) != 0) {
1375 C_SaferCond cond_ctx
;
1377 RWLock::RLocker
owner_lock(m_image_ctx
.owner_lock
);
1378 r
= prepare_image_update(true);
1383 execute_update_features(features
, enabled
, &cond_ctx
, 0);
1386 r
= cond_ctx
.wait();
1388 r
= invoke_async_request("update_features", false,
1389 boost::bind(&Operations
<I
>::execute_update_features
,
1390 this, features
, enabled
, _1
, 0),
1391 boost::bind(&ImageWatcher
<I
>::notify_update_features
,
1392 m_image_ctx
.image_watcher
, features
,
1395 ldout(cct
, 2) << "update_features finished" << dendl
;
1399 template <typename I
>
1400 void Operations
<I
>::execute_update_features(uint64_t features
, bool enabled
,
1402 uint64_t journal_op_tid
) {
1403 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
1404 ceph_assert(m_image_ctx
.exclusive_lock
== nullptr ||
1405 m_image_ctx
.exclusive_lock
->is_lock_owner());
1407 CephContext
*cct
= m_image_ctx
.cct
;
1408 ldout(cct
, 5) << this << " " << __func__
<< ": features=" << features
1409 << ", enabled=" << enabled
<< dendl
;
1411 if (m_image_ctx
.operations_disabled
) {
1412 on_finish
->complete(-EROFS
);
1417 operation::EnableFeaturesRequest
<I
> *req
=
1418 new operation::EnableFeaturesRequest
<I
>(
1419 m_image_ctx
, on_finish
, journal_op_tid
, features
);
1422 operation::DisableFeaturesRequest
<I
> *req
=
1423 new operation::DisableFeaturesRequest
<I
>(
1424 m_image_ctx
, on_finish
, journal_op_tid
, features
, false);
1429 template <typename I
>
1430 int Operations
<I
>::metadata_set(const std::string
&key
,
1431 const std::string
&value
) {
1432 CephContext
*cct
= m_image_ctx
.cct
;
1433 ldout(cct
, 5) << this << " " << __func__
<< ": key=" << key
<< ", value="
1436 std::string config_key
;
1437 bool config_override
= util::is_metadata_config_override(key
, &config_key
);
1438 if (config_override
) {
1439 // validate config setting
1440 if (!librbd::api::Config
<I
>::is_option_name(&m_image_ctx
, config_key
)) {
1441 lderr(cct
) << "validation for " << key
1442 << " failed: not allowed image level override" << dendl
;
1445 int r
= ConfigProxy
{false}.set_val(config_key
.c_str(), value
);
1451 int r
= m_image_ctx
.state
->refresh_if_required();
1456 if (m_image_ctx
.read_only
) {
1460 C_SaferCond metadata_ctx
;
1462 RWLock::RLocker
owner_lock(m_image_ctx
.owner_lock
);
1463 r
= prepare_image_update(true);
1468 execute_metadata_set(key
, value
, &metadata_ctx
);
1471 r
= metadata_ctx
.wait();
1472 if (config_override
&& r
>= 0) {
1473 // apply new config key immediately
1474 r
= m_image_ctx
.state
->refresh_if_required();
1480 template <typename I
>
1481 void Operations
<I
>::execute_metadata_set(const std::string
&key
,
1482 const std::string
&value
,
1483 Context
*on_finish
) {
1484 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
1486 CephContext
*cct
= m_image_ctx
.cct
;
1487 ldout(cct
, 5) << this << " " << __func__
<< ": key=" << key
<< ", value="
1490 if (m_image_ctx
.operations_disabled
) {
1491 on_finish
->complete(-EROFS
);
1495 operation::MetadataSetRequest
<I
> *request
=
1496 new operation::MetadataSetRequest
<I
>(m_image_ctx
,
1497 new C_NotifyUpdate
<I
>(m_image_ctx
, on_finish
),
1502 template <typename I
>
1503 int Operations
<I
>::metadata_remove(const std::string
&key
) {
1504 CephContext
*cct
= m_image_ctx
.cct
;
1505 ldout(cct
, 5) << this << " " << __func__
<< ": key=" << key
<< dendl
;
1507 int r
= m_image_ctx
.state
->refresh_if_required();
1512 if (m_image_ctx
.read_only
) {
1517 r
= cls_client::metadata_get(&m_image_ctx
.md_ctx
, m_image_ctx
.header_oid
, key
, &value
);
1521 C_SaferCond metadata_ctx
;
1523 RWLock::RLocker
owner_lock(m_image_ctx
.owner_lock
);
1524 r
= prepare_image_update(true);
1529 execute_metadata_remove(key
, &metadata_ctx
);
1532 r
= metadata_ctx
.wait();
1534 std::string config_key
;
1535 if (util::is_metadata_config_override(key
, &config_key
) && r
>= 0) {
1536 // apply new config key immediately
1537 r
= m_image_ctx
.state
->refresh_if_required();
1543 template <typename I
>
1544 void Operations
<I
>::execute_metadata_remove(const std::string
&key
,
1545 Context
*on_finish
) {
1546 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
1548 CephContext
*cct
= m_image_ctx
.cct
;
1549 ldout(cct
, 5) << this << " " << __func__
<< ": key=" << key
<< dendl
;
1551 if (m_image_ctx
.operations_disabled
) {
1552 on_finish
->complete(-EROFS
);
1556 operation::MetadataRemoveRequest
<I
> *request
=
1557 new operation::MetadataRemoveRequest
<I
>(
1559 new C_NotifyUpdate
<I
>(m_image_ctx
, on_finish
), key
);
1563 template <typename I
>
1564 int Operations
<I
>::migrate(ProgressContext
&prog_ctx
) {
1565 CephContext
*cct
= m_image_ctx
.cct
;
1566 ldout(cct
, 20) << "migrate" << dendl
;
1568 int r
= m_image_ctx
.state
->refresh_if_required();
1573 if (m_image_ctx
.read_only
) {
1578 RWLock::RLocker
parent_locker(m_image_ctx
.parent_lock
);
1579 if (m_image_ctx
.migration_info
.empty()) {
1580 lderr(cct
) << "image has no migrating parent" << dendl
;
1585 uint64_t request_id
= ++m_async_request_seq
;
1586 r
= invoke_async_request("migrate", false,
1587 boost::bind(&Operations
<I
>::execute_migrate
, this,
1588 boost::ref(prog_ctx
), _1
),
1589 boost::bind(&ImageWatcher
<I
>::notify_migrate
,
1590 m_image_ctx
.image_watcher
, request_id
,
1591 boost::ref(prog_ctx
), _1
));
1593 if (r
< 0 && r
!= -EINVAL
) {
1596 ldout(cct
, 20) << "migrate finished" << dendl
;
1600 template <typename I
>
1601 void Operations
<I
>::execute_migrate(ProgressContext
&prog_ctx
,
1602 Context
*on_finish
) {
1603 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
1604 ceph_assert(m_image_ctx
.exclusive_lock
== nullptr ||
1605 m_image_ctx
.exclusive_lock
->is_lock_owner());
1607 CephContext
*cct
= m_image_ctx
.cct
;
1608 ldout(cct
, 20) << "migrate" << dendl
;
1610 if (m_image_ctx
.read_only
|| m_image_ctx
.operations_disabled
) {
1611 on_finish
->complete(-EROFS
);
1615 m_image_ctx
.snap_lock
.get_read();
1616 m_image_ctx
.parent_lock
.get_read();
1618 if (m_image_ctx
.migration_info
.empty()) {
1619 lderr(cct
) << "image has no migrating parent" << dendl
;
1620 m_image_ctx
.parent_lock
.put_read();
1621 m_image_ctx
.snap_lock
.put_read();
1622 on_finish
->complete(-EINVAL
);
1625 if (m_image_ctx
.snap_id
!= CEPH_NOSNAP
) {
1626 lderr(cct
) << "snapshots cannot be migrated" << dendl
;
1627 m_image_ctx
.parent_lock
.put_read();
1628 m_image_ctx
.snap_lock
.put_read();
1629 on_finish
->complete(-EROFS
);
1633 m_image_ctx
.parent_lock
.put_read();
1634 m_image_ctx
.snap_lock
.put_read();
1636 operation::MigrateRequest
<I
> *req
= new operation::MigrateRequest
<I
>(
1637 m_image_ctx
, new C_NotifyUpdate
<I
>(m_image_ctx
, on_finish
), prog_ctx
);
1641 template <typename I
>
1642 int Operations
<I
>::sparsify(size_t sparse_size
, ProgressContext
&prog_ctx
) {
1643 CephContext
*cct
= m_image_ctx
.cct
;
1644 ldout(cct
, 20) << "sparsify" << dendl
;
1646 if (sparse_size
< 4096 || sparse_size
> m_image_ctx
.get_object_size() ||
1647 (sparse_size
& (sparse_size
- 1)) != 0) {
1648 lderr(cct
) << "sparse size should be power of two not less than 4096"
1649 << " and not larger image object size" << dendl
;
1653 uint64_t request_id
= ++m_async_request_seq
;
1654 int r
= invoke_async_request("sparsify", false,
1655 boost::bind(&Operations
<I
>::execute_sparsify
,
1657 boost::ref(prog_ctx
), _1
),
1658 boost::bind(&ImageWatcher
<I
>::notify_sparsify
,
1659 m_image_ctx
.image_watcher
,
1660 request_id
, sparse_size
,
1661 boost::ref(prog_ctx
), _1
));
1662 if (r
< 0 && r
!= -EINVAL
) {
1665 ldout(cct
, 20) << "resparsify finished" << dendl
;
1669 template <typename I
>
1670 void Operations
<I
>::execute_sparsify(size_t sparse_size
,
1671 ProgressContext
&prog_ctx
,
1672 Context
*on_finish
) {
1673 ceph_assert(m_image_ctx
.owner_lock
.is_locked());
1674 ceph_assert(m_image_ctx
.exclusive_lock
== nullptr ||
1675 m_image_ctx
.exclusive_lock
->is_lock_owner());
1677 CephContext
*cct
= m_image_ctx
.cct
;
1678 ldout(cct
, 20) << "sparsify" << dendl
;
1680 if (m_image_ctx
.operations_disabled
) {
1681 on_finish
->complete(-EROFS
);
1685 auto req
= new operation::SparsifyRequest
<I
>(
1686 m_image_ctx
, sparse_size
, new C_NotifyUpdate
<I
>(m_image_ctx
, on_finish
),
1691 template <typename I
>
1692 int Operations
<I
>::prepare_image_update(bool request_lock
) {
1693 ceph_assert(m_image_ctx
.owner_lock
.is_locked() &&
1694 !m_image_ctx
.owner_lock
.is_wlocked());
1695 if (m_image_ctx
.image_watcher
== nullptr) {
1699 // need to upgrade to a write lock
1701 m_image_ctx
.owner_lock
.put_read();
1702 bool attempting_lock
= false;
1704 RWLock::WLocker
owner_locker(m_image_ctx
.owner_lock
);
1705 if (m_image_ctx
.exclusive_lock
!= nullptr &&
1706 (!m_image_ctx
.exclusive_lock
->is_lock_owner() ||
1707 !m_image_ctx
.exclusive_lock
->accept_requests())) {
1709 attempting_lock
= true;
1710 m_image_ctx
.exclusive_lock
->block_requests(0);
1713 m_image_ctx
.exclusive_lock
->acquire_lock(&ctx
);
1715 m_image_ctx
.exclusive_lock
->try_acquire_lock(&ctx
);
1721 if (attempting_lock
) {
1725 m_image_ctx
.owner_lock
.get_read();
1726 if (attempting_lock
&& m_image_ctx
.exclusive_lock
!= nullptr) {
1727 m_image_ctx
.exclusive_lock
->unblock_requests();
1730 if (r
== -EAGAIN
|| r
== -EBUSY
) {
1735 } else if (m_image_ctx
.exclusive_lock
!= nullptr &&
1736 !m_image_ctx
.exclusive_lock
->is_lock_owner()) {
1737 return m_image_ctx
.exclusive_lock
->get_unlocked_op_error();
1743 template <typename I
>
1744 int Operations
<I
>::invoke_async_request(const std::string
& request_type
,
1745 bool permit_snapshot
,
1746 const boost::function
<void(Context
*)>& local_request
,
1747 const boost::function
<void(Context
*)>& remote_request
) {
1749 C_InvokeAsyncRequest
<I
> *req
= new C_InvokeAsyncRequest
<I
>(m_image_ctx
,
1759 } // namespace librbd
1761 template class librbd::Operations
<librbd::ImageCtx
>;