From: Ilya Dryomov Date: Fri, 14 Oct 2022 14:20:24 +0000 (+0200) Subject: librbd: introduce reduce_parent_overlap() and switch overlap API X-Git-Tag: v18.1.0~754^2~12 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=c97b7c4e8b366c00108f50781b7f45d5324a5c80;p=ceph.git librbd: introduce reduce_parent_overlap() and switch overlap API When encryption is loaded, rbd_get_overlap() and Image::overlap() now return "effective" overlap, similar to rbd_get_size() and Image::size(). Previously, returned overlap could have been bigger than "effective" size. Note that get_effective_image_size() successor doesn't take snap_id because passing anything but ictx->snap_id was broken. Since the size of the crypto header is stored in the crypto header itself, image areas are defined only for the "opened at" snap_id. Getting "effective" size for an arbitrary snapshot requires actually opening it and loading encryption on it. Signed-off-by: Ilya Dryomov --- diff --git a/src/librbd/ImageCtx.cc b/src/librbd/ImageCtx.cc index 1ca88b4168ce2..8f6413d0e405b 100644 --- a/src/librbd/ImageCtx.cc +++ b/src/librbd/ImageCtx.cc @@ -537,13 +537,26 @@ librados::IoCtx duplicate_io_ctx(librados::IoCtx& io_ctx) { return 0; } - uint64_t ImageCtx::get_effective_image_size(snap_t in_snap_id) const { - auto raw_size = get_image_size(in_snap_id); + uint64_t ImageCtx::get_area_size(io::ImageArea area) const { + // image areas are defined only for the "opened at" snap_id + // (i.e. where encryption may be loaded) + uint64_t raw_size = get_image_size(snap_id); if (raw_size == 0) { return 0; } - return io::util::raw_to_area_offset(*this, raw_size).first; + auto [data_size, data_area] = io::util::raw_to_area_offset(*this, raw_size); + ceph_assert(data_size <= raw_size && data_area == io::ImageArea::DATA); + + switch (area) { + case io::ImageArea::DATA: + return data_size; + case io::ImageArea::CRYPTO_HEADER: + // CRYPTO_HEADER area ends where DATA area begins + return raw_size - data_size; + default: + ceph_abort(); + } } uint64_t ImageCtx::get_object_count(snap_t in_snap_id) const { @@ -682,6 +695,29 @@ librados::IoCtx duplicate_io_ctx(librados::IoCtx& io_ctx) { return -ENOENT; } + std::pair ImageCtx::reduce_parent_overlap( + uint64_t raw_overlap, bool migration_write) const { + ceph_assert(ceph_mutex_is_locked(image_lock)); + if (migration_write) { + // don't reduce migration write overlap -- it may be larger as + // it's the largest overlap across snapshots by construction + return io::util::remap_offset(*this, raw_overlap); + } + if (raw_overlap == 0 || parent == nullptr) { + // image opened with OPEN_FLAG_SKIP_OPEN_PARENT -> no overlap + return io::util::remap_offset(*this, 0); + } + // DATA area in the parent may be smaller than the part of DATA + // area in the clone that is still within the overlap (e.g. for + // LUKS2-encrypted parent + LUKS1-encrypted clone, due to LUKS2 + // header usually being bigger than LUKS1 header) + auto overlap = io::util::remap_offset(*this, raw_overlap); + std::shared_lock parent_image_locker(parent->image_lock); + overlap.first = std::min(overlap.first, + parent->get_area_size(overlap.second)); + return overlap; + } + void ImageCtx::register_watch(Context *on_finish) { ceph_assert(image_watcher != NULL); image_watcher->register_watch(on_finish); diff --git a/src/librbd/ImageCtx.h b/src/librbd/ImageCtx.h index f9ce6dd16da0b..8c69615a7178f 100644 --- a/src/librbd/ImageCtx.h +++ b/src/librbd/ImageCtx.h @@ -61,6 +61,7 @@ namespace librbd { class AioCompletion; class AsyncOperation; template class CopyupRequest; + enum class ImageArea; struct ImageDispatcherInterface; struct ObjectDispatcherInterface; } @@ -301,7 +302,7 @@ namespace librbd { std::string in_snap_name, librados::snap_t id); uint64_t get_image_size(librados::snap_t in_snap_id) const; - uint64_t get_effective_image_size(librados::snap_t in_snap_id) const; + uint64_t get_area_size(io::ImageArea area) const; uint64_t get_object_count(librados::snap_t in_snap_id) const; bool test_features(uint64_t test_features) const; bool test_features(uint64_t test_features, @@ -323,6 +324,8 @@ namespace librbd { uint64_t get_parent_snap_id(librados::snap_t in_snap_id) const; int get_parent_overlap(librados::snap_t in_snap_id, uint64_t* raw_overlap) const; + std::pair reduce_parent_overlap( + uint64_t raw_overlap, bool migration_write) const; void register_watch(Context *on_finish); uint64_t prune_parent_extents(std::vector >& objectx, uint64_t overlap); diff --git a/src/librbd/internal.cc b/src/librbd/internal.cc index 02d95d17d2ae1..0cefd4b3603d2 100644 --- a/src/librbd/internal.cc +++ b/src/librbd/internal.cc @@ -182,7 +182,7 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) { int obj_order = ictx->order; { std::shared_lock locker{ictx->image_lock}; - info.size = ictx->get_effective_image_size(ictx->snap_id); + info.size = ictx->get_area_size(io::ImageArea::DATA); } info.obj_size = 1ULL << obj_order; info.num_objs = Striper::get_num_objects(ictx->layout, info.size); @@ -859,7 +859,7 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) { if (r < 0) return r; std::shared_lock l2{ictx->image_lock}; - *size = ictx->get_effective_image_size(ictx->snap_id); + *size = ictx->get_area_size(io::ImageArea::DATA); return 0; } @@ -878,8 +878,16 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) { int r = ictx->state->refresh_if_required(); if (r < 0) return r; + std::shared_lock image_locker{ictx->image_lock}; - return ictx->get_parent_overlap(ictx->snap_id, overlap); + uint64_t raw_overlap; + r = ictx->get_parent_overlap(ictx->snap_id, &raw_overlap); + if (r < 0) { + return r; + } + auto _overlap = ictx->reduce_parent_overlap(raw_overlap, false); + *overlap = (_overlap.second == io::ImageArea::DATA ? _overlap.first : 0); + return 0; } int get_flags(ImageCtx *ictx, uint64_t *flags) @@ -1596,19 +1604,21 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) { ictx->get_snap_info(ictx->snap_id) == nullptr) { return -ENOENT; } - uint64_t image_size = ictx->get_effective_image_size(ictx->snap_id); // special-case "len == 0" requests: always valid if (*len == 0) return 0; + // TODO: pass area + uint64_t area_size = ictx->get_area_size(io::ImageArea::DATA); + // can't start past end - if (off >= image_size) + if (off >= area_size) return -EINVAL; // clip requests that extend past end to just end - if ((off + *len) > image_size) - *len = (size_t)(image_size - off); + if ((off + *len) > area_size) + *len = (size_t)(area_size - off); return 0; } diff --git a/src/librbd/io/ImageRequest.cc b/src/librbd/io/ImageRequest.cc index c7f09ef87ac14..0a032d25e9d83 100644 --- a/src/librbd/io/ImageRequest.cc +++ b/src/librbd/io/ImageRequest.cc @@ -145,10 +145,10 @@ void readahead(I *ictx, const Extents& image_extents, IOContext io_context) { return; } - uint64_t image_size = ictx->get_effective_image_size(ictx->snap_id); + uint64_t data_size = ictx->get_area_size(ImageArea::DATA); ictx->image_lock.unlock_shared(); - auto readahead_extent = ictx->readahead.update(image_extents, image_size); + auto readahead_extent = ictx->readahead.update(image_extents, data_size); uint64_t readahead_offset = readahead_extent.first; uint64_t readahead_length = readahead_extent.second; diff --git a/src/test/librbd/mock/MockImageCtx.h b/src/test/librbd/mock/MockImageCtx.h index 6d84521616b6c..a4c3fc0326ed8 100644 --- a/src/test/librbd/mock/MockImageCtx.h +++ b/src/test/librbd/mock/MockImageCtx.h @@ -69,7 +69,7 @@ struct MockImageCtx { MOCK_CONST_METHOD0(get_object_size, uint64_t()); MOCK_CONST_METHOD0(get_current_size, uint64_t()); MOCK_CONST_METHOD1(get_image_size, uint64_t(librados::snap_t)); - MOCK_CONST_METHOD1(get_effective_image_size, uint64_t(librados::snap_t)); + MOCK_CONST_METHOD1(get_area_size, uint64_t(io::ImageArea)); MOCK_CONST_METHOD1(get_object_count, uint64_t(librados::snap_t)); MOCK_CONST_METHOD1(get_read_flags, int(librados::snap_t)); MOCK_CONST_METHOD2(get_flags, int(librados::snap_t in_snap_id, @@ -86,6 +86,8 @@ struct MockImageCtx { MOCK_CONST_METHOD1(get_parent_info, const ParentImageInfo*(librados::snap_t)); MOCK_CONST_METHOD2(get_parent_overlap, int(librados::snap_t in_snap_id, uint64_t *raw_overlap)); + MOCK_CONST_METHOD2(reduce_parent_overlap, + std::pair(uint64_t, bool)); MOCK_CONST_METHOD2(prune_parent_extents, uint64_t(std::vector >& , uint64_t));