diff options
Diffstat (limited to '')
-rw-r--r-- | src/librbd/object_map/DiffRequest.cc | 382 |
1 files changed, 249 insertions, 133 deletions
diff --git a/src/librbd/object_map/DiffRequest.cc b/src/librbd/object_map/DiffRequest.cc index 606d48bbf..acaf31a39 100644 --- a/src/librbd/object_map/DiffRequest.cc +++ b/src/librbd/object_map/DiffRequest.cc @@ -21,6 +21,193 @@ namespace object_map { using util::create_rados_callback; template <typename I> +DiffRequest<I>::DiffRequest(I* image_ctx, + uint64_t snap_id_start, uint64_t snap_id_end, + uint64_t start_object_no, uint64_t end_object_no, + BitVector<2>* object_diff_state, + Context* on_finish) + : m_image_ctx(image_ctx), m_snap_id_start(snap_id_start), + m_snap_id_end(snap_id_end), m_start_object_no(start_object_no), + m_end_object_no(end_object_no), m_object_diff_state(object_diff_state), + m_on_finish(on_finish) { + auto cct = m_image_ctx->cct; + ldout(cct, 10) << "snap_id_start=" << m_snap_id_start + << ", snap_id_end=" << m_snap_id_end + << ", start_object_no=" << m_start_object_no + << ", end_object_no=" << m_end_object_no + << dendl; +} + +template <typename I> +bool DiffRequest<I>::is_diff_iterate() const { + return m_start_object_no != 0 || m_end_object_no != UINT64_MAX; +} + +template <typename I> +int DiffRequest<I>::prepare_for_object_map() { + ceph_assert(ceph_mutex_is_locked(m_image_ctx->image_lock)); + + auto cct = m_image_ctx->cct; + ldout(cct, 20) << "snap_id=" << m_current_snap_id << dendl; + + if ((m_image_ctx->features & RBD_FEATURE_FAST_DIFF) == 0) { + ldout(cct, 10) << "fast-diff feature not enabled" << dendl; + return -EINVAL; + } + + if (m_current_snap_id == CEPH_NOSNAP) { + m_current_size = m_image_ctx->size; + } else { + auto snap_it = m_image_ctx->snap_info.find(m_current_snap_id); + if (snap_it == m_image_ctx->snap_info.end()) { + ldout(cct, 10) << "snapshot " << m_current_snap_id << " does not exist" + << dendl; + return -ENOENT; + } + m_current_size = snap_it->second.size; + } + + uint64_t flags; + int r = m_image_ctx->get_flags(m_current_snap_id, &flags); + ceph_assert(r == 0); + + if ((flags & RBD_FLAG_FAST_DIFF_INVALID) != 0) { + ldout(cct, 1) << "cannot perform fast diff on invalid object map" + << dendl; + return -EINVAL; + } + + return 0; +} + +template <typename I> +int DiffRequest<I>::process_object_map(const BitVector<2>& object_map) { + auto cct = m_image_ctx->cct; + ldout(cct, 20) << "snap_id=" << m_current_snap_id << dendl; + + uint64_t num_objs = Striper::get_num_objects(m_image_ctx->layout, + m_current_size); + if (object_map.size() < num_objs) { + ldout(cct, 1) << "object map too small: " + << object_map.size() << " < " << num_objs << dendl; + return -EINVAL; + } + + uint64_t start_object_no, end_object_no; + uint64_t prev_object_diff_state_size = m_object_diff_state->size(); + if (is_diff_iterate()) { + start_object_no = std::min(m_start_object_no, num_objs); + end_object_no = std::min(m_end_object_no, num_objs); + uint64_t num_objs_in_range = end_object_no - start_object_no; + if (m_object_diff_state->size() != num_objs_in_range) { + m_object_diff_state->resize(num_objs_in_range); + } + } else { + // for deep-copy, the object diff state should be the largest of + // all versions in the set, so it's only ever grown + // shrink is handled by flagging trimmed objects as non-existent + // and comparing against the previous object diff state as usual + if (m_object_diff_state->size() < num_objs) { + m_object_diff_state->resize(num_objs); + } + start_object_no = 0; + end_object_no = m_object_diff_state->size(); + } + + uint64_t overlap = std::min(m_object_diff_state->size(), + prev_object_diff_state_size); + auto it = object_map.begin() + start_object_no; + auto diff_it = m_object_diff_state->begin(); + uint64_t ono = start_object_no; + for (; ono < start_object_no + overlap; ++diff_it, ++ono) { + uint8_t object_map_state = (ono < num_objs ? *it++ : OBJECT_NONEXISTENT); + uint8_t prev_object_diff_state = *diff_it; + switch (prev_object_diff_state) { + case DIFF_STATE_HOLE: + if (object_map_state != OBJECT_NONEXISTENT) { + // stay in HOLE on intermediate snapshots for diff-iterate + if (!is_diff_iterate() || m_current_snap_id == m_snap_id_end) { + *diff_it = DIFF_STATE_DATA_UPDATED; + } + } + break; + case DIFF_STATE_DATA: + if (object_map_state == OBJECT_NONEXISTENT) { + *diff_it = DIFF_STATE_HOLE_UPDATED; + } else if (object_map_state != OBJECT_EXISTS_CLEAN) { + *diff_it = DIFF_STATE_DATA_UPDATED; + } + break; + case DIFF_STATE_HOLE_UPDATED: + if (object_map_state != OBJECT_NONEXISTENT) { + *diff_it = DIFF_STATE_DATA_UPDATED; + } + break; + case DIFF_STATE_DATA_UPDATED: + if (object_map_state == OBJECT_NONEXISTENT) { + *diff_it = DIFF_STATE_HOLE_UPDATED; + } + break; + default: + ceph_abort(); + } + + ldout(cct, 20) << "object state: " << ono << " " + << static_cast<uint32_t>(prev_object_diff_state) + << "->" << static_cast<uint32_t>(*diff_it) << " (" + << static_cast<uint32_t>(object_map_state) << ")" + << dendl; + } + ldout(cct, 20) << "computed overlap diffs" << dendl; + + ceph_assert(diff_it == m_object_diff_state->end() || + end_object_no <= num_objs); + for (; ono < end_object_no; ++it, ++diff_it, ++ono) { + uint8_t object_map_state = *it; + if (object_map_state == OBJECT_NONEXISTENT) { + *diff_it = DIFF_STATE_HOLE; + } else if (m_current_snap_id != m_snap_id_start) { + // diffing against the beginning of time or image was grown + // (implicit) starting state is HOLE, this is the first object + // map after + if (is_diff_iterate()) { + // for diff-iterate, if the object is discarded prior to or + // in the end version, result should be HOLE + // since DATA_UPDATED can transition only to HOLE_UPDATED, + // stay in HOLE on intermediate snapshots -- another way to + // put this is that when starting with a hole, intermediate + // snapshots can be ignored as the result depends only on the + // end version + if (m_current_snap_id == m_snap_id_end) { + *diff_it = DIFF_STATE_DATA_UPDATED; + } else { + *diff_it = DIFF_STATE_HOLE; + } + } else { + // for deep-copy, if the object is discarded prior to or + // in the end version, result should be HOLE_UPDATED + *diff_it = DIFF_STATE_DATA_UPDATED; + } + } else { + // diffing against a snapshot, this is its object map + if (object_map_state != OBJECT_PENDING) { + *diff_it = DIFF_STATE_DATA; + } else { + *diff_it = DIFF_STATE_DATA_UPDATED; + } + } + + ldout(cct, 20) << "object state: " << ono << " " + << "->" << static_cast<uint32_t>(*diff_it) << " (" + << static_cast<uint32_t>(*it) << ")" << dendl; + } + ldout(cct, 20) << "computed resize diffs" << dendl; + + ceph_assert(diff_it == m_object_diff_state->end()); + return 0; +} + +template <typename I> void DiffRequest<I>::send() { auto cct = m_image_ctx->cct; @@ -30,24 +217,62 @@ void DiffRequest<I>::send() { << "snap_id_end=" << m_snap_id_end << dendl; finish(-EINVAL); return; - } else if (m_snap_id_start == m_snap_id_end) { - // no delta between the same snapshot - finish(0); + } + if (m_start_object_no == UINT64_MAX || m_start_object_no > m_end_object_no || + (m_start_object_no != 0 && m_end_object_no == UINT64_MAX)) { + lderr(cct) << "invalid start/end object numbers: " + << "start_object_no=" << m_start_object_no << ", " + << "end_object_no=" << m_end_object_no << dendl; + finish(-EINVAL); return; } m_object_diff_state->clear(); - // collect all the snap ids in the provided range (inclusive) - if (m_snap_id_start != 0) { - m_snap_ids.insert(m_snap_id_start); + if (m_snap_id_start == m_snap_id_end) { + // no delta between the same snapshot + finish(0); + return; + } + if (m_start_object_no == m_end_object_no) { + // no objects in the provided range (half-open) + finish(0); + return; } std::shared_lock image_locker{m_image_ctx->image_lock}; - auto snap_info_it = m_image_ctx->snap_info.upper_bound(m_snap_id_start); - auto snap_info_it_end = m_image_ctx->snap_info.lower_bound(m_snap_id_end); - for (; snap_info_it != snap_info_it_end; ++snap_info_it) { - m_snap_ids.insert(snap_info_it->first); + if (is_diff_iterate() && + m_snap_id_start == 0 && + m_snap_id_end == m_image_ctx->snap_id && + m_image_ctx->object_map != nullptr) { + ldout(cct, 10) << "using in-memory object map" << dendl; + m_current_snap_id = m_snap_id_end; + + int r = prepare_for_object_map(); + if (r == 0) { + r = m_image_ctx->object_map->with_object_map( + [this](const BitVector<2>& object_map) { + return process_object_map(object_map); + }); + } + image_locker.unlock(); + + finish(r); + return; + } + + // collect all the snap ids in the provided range (inclusive) unless + // this is diff-iterate against the beginning of time, in which case + // only the end version matters + if (!is_diff_iterate() || m_snap_id_start != 0) { + if (m_snap_id_start != 0) { + m_snap_ids.insert(m_snap_id_start); + } + auto snap_info_it = m_image_ctx->snap_info.upper_bound(m_snap_id_start); + auto snap_info_it_end = m_image_ctx->snap_info.lower_bound(m_snap_id_end); + for (; snap_info_it != snap_info_it_end; ++snap_info_it) { + m_snap_ids.insert(snap_info_it->first); + } } m_snap_ids.insert(m_snap_id_end); @@ -72,59 +297,23 @@ void DiffRequest<I>::load_object_map( auto cct = m_image_ctx->cct; ldout(cct, 10) << "snap_id=" << m_current_snap_id << dendl; - if ((m_image_ctx->features & RBD_FEATURE_FAST_DIFF) == 0) { - image_locker->unlock(); - - ldout(cct, 10) << "fast-diff feature not enabled" << dendl; - finish(-EINVAL); - return; - } - // ignore ENOENT with intermediate snapshots since deleted // snaps will get merged with later snapshots m_ignore_enoent = (m_current_snap_id != m_snap_id_start && m_current_snap_id != m_snap_id_end); - if (m_current_snap_id == CEPH_NOSNAP) { - m_current_size = m_image_ctx->size; - } else { - auto snap_it = m_image_ctx->snap_info.find(m_current_snap_id); - if (snap_it == m_image_ctx->snap_info.end()) { - ldout(cct, 10) << "snapshot " << m_current_snap_id << " does not exist" - << dendl; - if (!m_ignore_enoent) { - image_locker->unlock(); - - finish(-ENOENT); - return; - } - - load_object_map(image_locker); - return; - } - - m_current_size = snap_it->second.size; - } - - uint64_t flags = 0; - int r = m_image_ctx->get_flags(m_current_snap_id, &flags); - if (r < 0) { + int r = prepare_for_object_map(); + if (r == -ENOENT && m_ignore_enoent) { + load_object_map(image_locker); + return; + } else if (r < 0) { image_locker->unlock(); - lderr(cct) << "failed to retrieve image flags: " << cpp_strerror(r) - << dendl; finish(r); return; } image_locker->unlock(); - if ((flags & RBD_FLAG_FAST_DIFF_INVALID) != 0) { - ldout(cct, 1) << "cannot perform fast diff on invalid object map" - << dendl; - finish(-EINVAL); - return; - } - std::string oid(ObjectMap<>::object_map_name(m_image_ctx->id, m_current_snap_id)); @@ -144,100 +333,27 @@ void DiffRequest<I>::handle_load_object_map(int r) { auto cct = m_image_ctx->cct; ldout(cct, 10) << "r=" << r << dendl; + BitVector<2> object_map; + std::string oid(ObjectMap<>::object_map_name(m_image_ctx->id, + m_current_snap_id)); + if (r == 0) { auto bl_it = m_out_bl.cbegin(); - r = cls_client::object_map_load_finish(&bl_it, &m_object_map); + r = cls_client::object_map_load_finish(&bl_it, &object_map); } - - std::string oid(ObjectMap<>::object_map_name(m_image_ctx->id, - m_current_snap_id)); if (r == -ENOENT && m_ignore_enoent) { ldout(cct, 10) << "object map " << oid << " does not exist" << dendl; - - std::shared_lock image_locker{m_image_ctx->image_lock}; - load_object_map(&image_locker); - return; } else if (r < 0) { lderr(cct) << "failed to load object map: " << oid << dendl; finish(r); return; - } - ldout(cct, 20) << "loaded object map " << oid << dendl; - - uint64_t num_objs = Striper::get_num_objects(m_image_ctx->layout, - m_current_size); - if (m_object_map.size() < num_objs) { - ldout(cct, 1) << "object map too small: " - << m_object_map.size() << " < " << num_objs << dendl; - finish(-EINVAL); - return; } else { - m_object_map.resize(num_objs); - } - - uint64_t prev_object_diff_state_size = m_object_diff_state->size(); - if (prev_object_diff_state_size < num_objs) { - // the diff state should be the largest of all snapshots in the set - m_object_diff_state->resize(num_objs); - } - if (m_object_map.size() < m_object_diff_state->size()) { - // the image was shrunk so expanding the object map will flag end objects - // as non-existent and they will be compared against the previous object - // diff state - m_object_map.resize(m_object_diff_state->size()); - } - - uint64_t overlap = std::min(m_object_map.size(), prev_object_diff_state_size); - auto it = m_object_map.begin(); - auto overlap_end_it = it + overlap; - auto diff_it = m_object_diff_state->begin(); - uint64_t i = 0; - for (; it != overlap_end_it; ++it, ++diff_it, ++i) { - uint8_t object_map_state = *it; - uint8_t prev_object_diff_state = *diff_it; - if (object_map_state == OBJECT_EXISTS || - object_map_state == OBJECT_PENDING || - (object_map_state == OBJECT_EXISTS_CLEAN && - prev_object_diff_state != DIFF_STATE_DATA && - prev_object_diff_state != DIFF_STATE_DATA_UPDATED)) { - *diff_it = DIFF_STATE_DATA_UPDATED; - } else if (object_map_state == OBJECT_NONEXISTENT && - prev_object_diff_state != DIFF_STATE_HOLE && - prev_object_diff_state != DIFF_STATE_HOLE_UPDATED) { - *diff_it = DIFF_STATE_HOLE_UPDATED; - } - - ldout(cct, 20) << "object state: " << i << " " - << static_cast<uint32_t>(prev_object_diff_state) - << "->" << static_cast<uint32_t>(*diff_it) << " (" - << static_cast<uint32_t>(object_map_state) << ")" - << dendl; - } - ldout(cct, 20) << "computed overlap diffs" << dendl; - - bool diff_from_start = (m_snap_id_start == 0); - auto end_it = m_object_map.end(); - if (m_object_map.size() > prev_object_diff_state_size) { - for (; it != end_it; ++it,++diff_it, ++i) { - uint8_t object_map_state = *it; - if (object_map_state == OBJECT_NONEXISTENT) { - *diff_it = DIFF_STATE_HOLE; - } else if (diff_from_start || - (m_object_diff_state_valid && - object_map_state != OBJECT_EXISTS_CLEAN)) { - *diff_it = DIFF_STATE_DATA_UPDATED; - } else { - *diff_it = DIFF_STATE_DATA; - } - - ldout(cct, 20) << "object state: " << i << " " - << "->" << static_cast<uint32_t>(*diff_it) << " (" - << static_cast<uint32_t>(*it) << ")" << dendl; + r = process_object_map(object_map); + if (r < 0) { + finish(r); + return; } } - ldout(cct, 20) << "computed resize diffs" << dendl; - - m_object_diff_state_valid = true; std::shared_lock image_locker{m_image_ctx->image_lock}; load_object_map(&image_locker); |