]>
Commit | Line | Data |
---|---|---|
9f95a23c TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "librbd/object_map/DiffRequest.h" | |
5 | #include "common/debug.h" | |
6 | #include "common/errno.h" | |
7 | #include "librbd/ImageCtx.h" | |
8 | #include "librbd/ObjectMap.h" | |
9 | #include "librbd/Utils.h" | |
10 | #include "osdc/Striper.h" | |
11 | #include <string> | |
12 | ||
13 | #define dout_subsys ceph_subsys_rbd | |
14 | #undef dout_prefix | |
15 | #define dout_prefix *_dout << "librbd::object_map::DiffRequest: " \ | |
16 | << this << " " << __func__ << ": " | |
17 | ||
18 | namespace librbd { | |
19 | namespace object_map { | |
20 | ||
21 | using util::create_rados_callback; | |
22 | ||
23 | template <typename I> | |
24 | void DiffRequest<I>::send() { | |
adb31ebb | 25 | auto cct = m_image_ctx->cct; |
9f95a23c | 26 | |
adb31ebb TL |
27 | if (m_snap_id_start == CEPH_NOSNAP || m_snap_id_start > m_snap_id_end) { |
28 | lderr(cct) << "invalid start/end snap ids: " | |
29 | << "snap_id_start=" << m_snap_id_start << ", " | |
30 | << "snap_id_end=" << m_snap_id_end << dendl; | |
31 | finish(-EINVAL); | |
32 | return; | |
33 | } else if (m_snap_id_start == m_snap_id_end) { | |
34 | // no delta between the same snapshot | |
35 | finish(0); | |
36 | return; | |
9f95a23c TL |
37 | } |
38 | ||
39 | m_object_diff_state->clear(); | |
adb31ebb TL |
40 | |
41 | // collect all the snap ids in the provided range (inclusive) | |
42 | if (m_snap_id_start != 0) { | |
43 | m_snap_ids.insert(m_snap_id_start); | |
44 | } | |
45 | ||
46 | std::shared_lock image_locker{m_image_ctx->image_lock}; | |
47 | auto snap_info_it = m_image_ctx->snap_info.upper_bound(m_snap_id_start); | |
48 | auto snap_info_it_end = m_image_ctx->snap_info.lower_bound(m_snap_id_end); | |
49 | for (; snap_info_it != snap_info_it_end; ++snap_info_it) { | |
50 | m_snap_ids.insert(snap_info_it->first); | |
51 | } | |
52 | m_snap_ids.insert(m_snap_id_end); | |
9f95a23c TL |
53 | |
54 | load_object_map(&image_locker); | |
55 | } | |
56 | ||
57 | template <typename I> | |
58 | void DiffRequest<I>::load_object_map( | |
59 | std::shared_lock<ceph::shared_mutex>* image_locker) { | |
60 | ceph_assert(ceph_mutex_is_locked(m_image_ctx->image_lock)); | |
61 | ||
adb31ebb TL |
62 | if (m_snap_ids.empty()) { |
63 | image_locker->unlock(); | |
9f95a23c | 64 | |
adb31ebb TL |
65 | finish(0); |
66 | return; | |
9f95a23c TL |
67 | } |
68 | ||
adb31ebb TL |
69 | m_current_snap_id = *m_snap_ids.begin(); |
70 | m_snap_ids.erase(m_current_snap_id); | |
71 | ||
72 | auto cct = m_image_ctx->cct; | |
73 | ldout(cct, 10) << "snap_id=" << m_current_snap_id << dendl; | |
74 | ||
75 | if ((m_image_ctx->features & RBD_FEATURE_FAST_DIFF) == 0) { | |
9f95a23c TL |
76 | image_locker->unlock(); |
77 | ||
78 | ldout(cct, 10) << "fast-diff feature not enabled" << dendl; | |
79 | finish(-EINVAL); | |
80 | return; | |
81 | } | |
82 | ||
adb31ebb TL |
83 | // ignore ENOENT with intermediate snapshots since deleted |
84 | // snaps will get merged with later snapshots | |
85 | m_ignore_enoent = (m_current_snap_id != m_snap_id_start && | |
86 | m_current_snap_id != m_snap_id_end); | |
87 | ||
88 | if (m_current_snap_id == CEPH_NOSNAP) { | |
89 | m_current_size = m_image_ctx->size; | |
90 | } else { | |
91 | auto snap_it = m_image_ctx->snap_info.find(m_current_snap_id); | |
92 | if (snap_it == m_image_ctx->snap_info.end()) { | |
93 | ldout(cct, 10) << "snapshot " << m_current_snap_id << " does not exist" | |
94 | << dendl; | |
95 | if (!m_ignore_enoent) { | |
96 | image_locker->unlock(); | |
97 | ||
98 | finish(-ENOENT); | |
99 | return; | |
100 | } | |
101 | ||
102 | load_object_map(image_locker); | |
103 | return; | |
104 | } | |
105 | ||
106 | m_current_size = snap_it->second.size; | |
107 | } | |
108 | ||
9f95a23c TL |
109 | uint64_t flags = 0; |
110 | int r = m_image_ctx->get_flags(m_current_snap_id, &flags); | |
111 | if (r < 0) { | |
112 | image_locker->unlock(); | |
113 | ||
114 | lderr(cct) << "failed to retrieve image flags: " << cpp_strerror(r) | |
115 | << dendl; | |
116 | finish(r); | |
117 | return; | |
118 | } | |
adb31ebb | 119 | image_locker->unlock(); |
9f95a23c | 120 | |
adb31ebb | 121 | if ((flags & RBD_FLAG_FAST_DIFF_INVALID) != 0) { |
9f95a23c TL |
122 | ldout(cct, 1) << "cannot perform fast diff on invalid object map" |
123 | << dendl; | |
124 | finish(-EINVAL); | |
125 | return; | |
126 | } | |
127 | ||
128 | std::string oid(ObjectMap<>::object_map_name(m_image_ctx->id, | |
129 | m_current_snap_id)); | |
130 | ||
131 | librados::ObjectReadOperation op; | |
132 | cls_client::object_map_load_start(&op); | |
133 | ||
134 | m_out_bl.clear(); | |
135 | auto aio_comp = create_rados_callback< | |
136 | DiffRequest<I>, &DiffRequest<I>::handle_load_object_map>(this); | |
137 | r = m_image_ctx->md_ctx.aio_operate(oid, aio_comp, &op, &m_out_bl); | |
138 | ceph_assert(r == 0); | |
139 | aio_comp->release(); | |
140 | } | |
141 | ||
142 | template <typename I> | |
143 | void DiffRequest<I>::handle_load_object_map(int r) { | |
144 | auto cct = m_image_ctx->cct; | |
145 | ldout(cct, 10) << "r=" << r << dendl; | |
146 | ||
147 | if (r == 0) { | |
148 | auto bl_it = m_out_bl.cbegin(); | |
149 | r = cls_client::object_map_load_finish(&bl_it, &m_object_map); | |
150 | } | |
151 | ||
152 | std::string oid(ObjectMap<>::object_map_name(m_image_ctx->id, | |
153 | m_current_snap_id)); | |
adb31ebb TL |
154 | if (r == -ENOENT && m_ignore_enoent) { |
155 | ldout(cct, 10) << "object map " << oid << " does not exist" << dendl; | |
156 | ||
157 | std::shared_lock image_locker{m_image_ctx->image_lock}; | |
158 | load_object_map(&image_locker); | |
159 | return; | |
160 | } else if (r < 0) { | |
9f95a23c TL |
161 | lderr(cct) << "failed to load object map: " << oid << dendl; |
162 | finish(r); | |
163 | return; | |
164 | } | |
165 | ldout(cct, 20) << "loaded object map " << oid << dendl; | |
166 | ||
167 | uint64_t num_objs = Striper::get_num_objects(m_image_ctx->layout, | |
168 | m_current_size); | |
169 | if (m_object_map.size() < num_objs) { | |
170 | ldout(cct, 1) << "object map too small: " | |
171 | << m_object_map.size() << " < " << num_objs << dendl; | |
172 | finish(-EINVAL); | |
173 | return; | |
174 | } else { | |
175 | m_object_map.resize(num_objs); | |
176 | } | |
177 | ||
cd265ab1 TL |
178 | size_t prev_object_diff_state_size = m_object_diff_state->size(); |
179 | if (prev_object_diff_state_size < num_objs) { | |
9f95a23c TL |
180 | // the diff state should be the largest of all snapshots in the set |
181 | m_object_diff_state->resize(num_objs); | |
182 | } | |
183 | if (m_object_map.size() < m_object_diff_state->size()) { | |
184 | // the image was shrunk so expanding the object map will flag end objects | |
185 | // as non-existent and they will be compared against the previous object | |
cd265ab1 | 186 | // diff state |
9f95a23c TL |
187 | m_object_map.resize(m_object_diff_state->size()); |
188 | } | |
189 | ||
cd265ab1 | 190 | uint64_t overlap = std::min(m_object_map.size(), prev_object_diff_state_size); |
9f95a23c TL |
191 | auto it = m_object_map.begin(); |
192 | auto overlap_end_it = it + overlap; | |
9f95a23c TL |
193 | auto diff_it = m_object_diff_state->begin(); |
194 | uint64_t i = 0; | |
cd265ab1 TL |
195 | for (; it != overlap_end_it; ++it, ++diff_it, ++i) { |
196 | uint8_t object_map_state = *it; | |
197 | uint8_t prev_object_diff_state = *diff_it; | |
198 | if (object_map_state == OBJECT_EXISTS || | |
199 | object_map_state == OBJECT_PENDING || | |
200 | (object_map_state == OBJECT_EXISTS_CLEAN && | |
201 | prev_object_diff_state != DIFF_STATE_DATA && | |
202 | prev_object_diff_state != DIFF_STATE_DATA_UPDATED)) { | |
203 | *diff_it = DIFF_STATE_DATA_UPDATED; | |
204 | } else if (object_map_state == OBJECT_NONEXISTENT && | |
205 | prev_object_diff_state != DIFF_STATE_HOLE && | |
206 | prev_object_diff_state != DIFF_STATE_HOLE_UPDATED) { | |
207 | *diff_it = DIFF_STATE_HOLE_UPDATED; | |
9f95a23c | 208 | } |
cd265ab1 TL |
209 | |
210 | ldout(cct, 20) << "object state: " << i << " " | |
211 | << static_cast<uint32_t>(prev_object_diff_state) | |
212 | << "->" << static_cast<uint32_t>(*diff_it) << " (" | |
213 | << static_cast<uint32_t>(object_map_state) << ")" | |
214 | << dendl; | |
9f95a23c TL |
215 | } |
216 | ldout(cct, 20) << "computed overlap diffs" << dendl; | |
217 | ||
adb31ebb | 218 | bool diff_from_start = (m_snap_id_start == 0); |
9f95a23c | 219 | auto end_it = m_object_map.end(); |
cd265ab1 | 220 | if (m_object_map.size() > prev_object_diff_state_size) { |
9f95a23c | 221 | for (; it != end_it; ++it,++diff_it, ++i) { |
cd265ab1 TL |
222 | uint8_t object_map_state = *it; |
223 | if (object_map_state == OBJECT_NONEXISTENT) { | |
224 | *diff_it = DIFF_STATE_HOLE; | |
a4b75251 TL |
225 | } else if (diff_from_start || |
226 | (m_object_diff_state_valid && | |
227 | object_map_state != OBJECT_EXISTS_CLEAN)) { | |
cd265ab1 | 228 | *diff_it = DIFF_STATE_DATA_UPDATED; |
9f95a23c | 229 | } else { |
cd265ab1 | 230 | *diff_it = DIFF_STATE_DATA; |
9f95a23c | 231 | } |
cd265ab1 TL |
232 | |
233 | ldout(cct, 20) << "object state: " << i << " " | |
234 | << "->" << static_cast<uint32_t>(*diff_it) << " (" | |
235 | << static_cast<uint32_t>(*it) << ")" << dendl; | |
9f95a23c TL |
236 | } |
237 | } | |
238 | ldout(cct, 20) << "computed resize diffs" << dendl; | |
239 | ||
cd265ab1 | 240 | m_object_diff_state_valid = true; |
9f95a23c TL |
241 | |
242 | std::shared_lock image_locker{m_image_ctx->image_lock}; | |
243 | load_object_map(&image_locker); | |
244 | } | |
245 | ||
246 | template <typename I> | |
247 | void DiffRequest<I>::finish(int r) { | |
248 | auto cct = m_image_ctx->cct; | |
249 | ldout(cct, 10) << "r=" << r << dendl; | |
250 | ||
251 | m_on_finish->complete(r); | |
252 | delete this; | |
253 | } | |
254 | ||
255 | } // namespace object_map | |
256 | } // namespace librbd | |
257 | ||
258 | template class librbd::object_map::DiffRequest<librbd::ImageCtx>; |