#include "common/errno.h"
#include "include/stringify.h"
#include "cls/lock/cls_lock_client.h"
+#include <sstream>
#define dout_subsys ceph_subsys_rbd
#undef dout_prefix
{
}
+std::string ObjectMap::object_map_name(const std::string &image_id,
+ uint64_t snap_id) {
+ std::string oid(RBD_OBJECT_MAP_PREFIX + image_id);
+ if (snap_id != CEPH_NOSNAP) {
+ std::stringstream snap_suffix;
+ snap_suffix << "." << std::setfill('0') << std::setw(16) << std::hex
+ << snap_id;
+ oid += snap_suffix.str();
+ }
+ return oid;
+}
+
int ObjectMap::lock()
{
if ((m_image_ctx.features & RBD_FEATURE_OBJECT_MAP) == 0) {
int r;
bool broke_lock = false;
CephContext *cct = m_image_ctx.cct;
+ std::string oid(object_map_name(m_image_ctx.id, CEPH_NOSNAP));
while (true) {
ldout(cct, 10) << &m_image_ctx << " locking object map" << dendl;
- r = rados::cls::lock::lock(&m_image_ctx.md_ctx,
- object_map_name(m_image_ctx.id),
+ r = rados::cls::lock::lock(&m_image_ctx.md_ctx, oid,
RBD_LOCK_NAME, LOCK_EXCLUSIVE, "", "", "",
utime_t(), 0);
if (r == 0) {
lockers_t lockers;
ClsLockType lock_type;
std::string lock_tag;
- int r = rados::cls::lock::get_lock_info(&m_image_ctx.md_ctx,
- object_map_name(m_image_ctx.id),
+ int r = rados::cls::lock::get_lock_info(&m_image_ctx.md_ctx, oid,
RBD_LOCK_NAME, &lockers,
&lock_type, &lock_tag);
if (r == -ENOENT) {
for (lockers_t::iterator it = lockers.begin();
it != lockers.end(); ++it) {
const rados::cls::lock::locker_id_t &locker = it->first;
- r = rados::cls::lock::break_lock(&m_image_ctx.md_ctx,
- object_map_name(m_image_ctx.id),
+ r = rados::cls::lock::break_lock(&m_image_ctx.md_ctx, oid,
RBD_LOCK_NAME, locker.cookie,
locker.locker);
if (r < 0 && r != -ENOENT) {
ldout(m_image_ctx.cct, 10) << &m_image_ctx << " unlocking object map"
<< dendl;
- int r = rados::cls::lock::unlock(&m_image_ctx.md_ctx,
- object_map_name(m_image_ctx.id),
+ std::string oid = object_map_name(m_image_ctx.id, CEPH_NOSNAP);
+ int r = rados::cls::lock::unlock(&m_image_ctx.md_ctx, oid,
RBD_LOCK_NAME, "");
if (r < 0 && r != -ENOENT) {
lderr(m_image_ctx.cct) << "failed to release object map lock: "
}
RWLock::RLocker l(m_image_ctx.object_map_lock);
- assert(object_no < object_map.size());
+ assert(object_no < m_object_map.size());
- bool exists = (object_map[object_no] == OBJECT_EXISTS ||
- object_map[object_no] == OBJECT_PENDING);
+ bool exists = (m_object_map[object_no] == OBJECT_EXISTS ||
+ m_object_map[object_no] == OBJECT_PENDING);
ldout(m_image_ctx.cct, 20) << &m_image_ctx << " object_may_exist: "
<< "object_no=" << object_no << " r=" << exists
<< dendl;
return exists;
}
-int ObjectMap::refresh()
+void ObjectMap::refresh(uint64_t snap_id)
{
if ((m_image_ctx.features & RBD_FEATURE_OBJECT_MAP) == 0) {
- return 0;
+ return;
}
CephContext *cct = m_image_ctx.cct;
ldout(cct, 10) << &m_image_ctx << " refreshing object map" << dendl;
RWLock::WLocker l(m_image_ctx.object_map_lock);
- int r = cls_client::object_map_load(&m_image_ctx.data_ctx,
- object_map_name(m_image_ctx.id),
- &object_map);
+ std::string oid(object_map_name(m_image_ctx.id, snap_id));
+ int r = cls_client::object_map_load(&m_image_ctx.md_ctx, oid,
+ &m_object_map);
if (r < 0) {
lderr(cct) << "error refreshing object map: " << cpp_strerror(r)
<< dendl;
invalidate();
- object_map.clear();
- return r;
+ m_object_map.clear();
+ return;
}
- ldout(cct, 20) << "refreshed object map: " << object_map.size()
+ ldout(cct, 20) << "refreshed object map: " << m_object_map.size()
<< dendl;
uint64_t num_objs = Striper::get_num_objects(
- m_image_ctx.layout, m_image_ctx.get_image_size(m_image_ctx.snap_id));
- if (object_map.size() != num_objs) {
+ m_image_ctx.layout, m_image_ctx.get_image_size(snap_id));
+ if (m_object_map.size() != num_objs) {
// resize op might have been interrupted
- lderr(cct) << "incorrect object map size: " << object_map.size()
+ lderr(cct) << "incorrect object map size: " << m_object_map.size()
<< " != " << num_objs << dendl;
invalidate();
- return -EINVAL;
}
- return 0;
}
+void ObjectMap::rollback(uint64_t snap_id) {
+ if ((m_image_ctx.features & RBD_FEATURE_OBJECT_MAP) == 0) {
+ return;
+ }
+
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << &m_image_ctx << " rollback object map" << dendl;
+
+ RWLock::WLocker l(m_image_ctx.object_map_lock);
+
+ std::string snap_oid(object_map_name(m_image_ctx.id, snap_id));
+ bufferlist bl;
+ int r = m_image_ctx.md_ctx.read(snap_oid, bl, 0, 0);
+ if (r < 0) {
+ lderr(cct) << "unable to load snapshot object map '" << snap_oid << "': "
+ << cpp_strerror(r) << dendl;
+ invalidate();
+ return;
+ }
+
+ librados::ObjectWriteOperation op;
+ rados::cls::lock::assert_locked(&op, RBD_LOCK_NAME, LOCK_EXCLUSIVE, "", "");
+ op.write_full(bl);
+
+ std::string oid(object_map_name(m_image_ctx.id, CEPH_NOSNAP));
+ r = m_image_ctx.md_ctx.operate(oid, &op);
+ if (r < 0) {
+ lderr(cct) << "unable to rollback object map: " << cpp_strerror(r)
+ << dendl;
+ invalidate();
+ }
+}
+
+void ObjectMap::snapshot(uint64_t snap_id) {
+ if ((m_image_ctx.features & RBD_FEATURE_OBJECT_MAP) == 0) {
+ return;
+ }
+
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << &m_image_ctx << " snapshot object map" << dendl;
+
+ int r;
+ bufferlist bl;
+ {
+ RWLock::RLocker l(m_image_ctx.object_map_lock);
+ std::string oid(object_map_name(m_image_ctx.id, CEPH_NOSNAP));
+ r = m_image_ctx.md_ctx.read(oid, bl, 0, 0);
+ if (r < 0) {
+ lderr(cct) << "unable to load object map: " << cpp_strerror(r)
+ << dendl;
+ invalidate();
+ }
+ }
+
+ std::string snap_oid(object_map_name(m_image_ctx.id, snap_id));
+ r = m_image_ctx.md_ctx.write_full(snap_oid, bl);
+ if (r < 0) {
+ lderr(cct) << "unable to snapshot object map '" << snap_oid << "': "
+ << cpp_strerror(r) << dendl;
+ invalidate();
+ }
+}
void ObjectMap::aio_resize(uint64_t new_size, uint8_t default_object_state,
Context *on_finish) {
ldout(cct, 20) << &m_image_ctx << " aio_update: start=" << start_object_no
<< ", end=" << end_object_no << ", new_state="
<< static_cast<uint32_t>(new_state) << dendl;
- if (end_object_no > object_map.size()) {
+ if (end_object_no > m_object_map.size()) {
ldout(cct, 20) << "skipping update of invalid object map" << dendl;
return false;
}
for (uint64_t object_no = start_object_no; object_no < end_object_no;
++object_no) {
- if ((!current_state || object_map[object_no] == *current_state) &&
- object_map[object_no] != new_state) {
+ if ((!current_state || m_object_map[object_no] == *current_state) &&
+ m_object_map[object_no] != new_state) {
UpdateRequest *req = new UpdateRequest(m_image_ctx, start_object_no,
end_object_no, new_state,
current_state, on_finish);
cls_client::object_map_resize(&op, m_num_objs, m_default_object_state);
librados::AioCompletion *rados_completion = create_callback_completion();
- int r = m_image_ctx.data_ctx.aio_operate(object_map_name(m_image_ctx.id),
- rados_completion, &op);
+ std::string oid(object_map_name(m_image_ctx.id, CEPH_NOSNAP));
+ int r = m_image_ctx.md_ctx.aio_operate(oid, rados_completion, &op);
assert(r == 0);
rados_completion->release();
}
ldout(cct, 5) << &m_image_ctx << " resizing in-memory object map: "
<< m_num_objs << dendl;
- size_t orig_object_map_size = object_map->object_map.size();
- object_map->object_map.resize(m_num_objs);
- for (uint64_t i = orig_object_map_size; i < object_map->object_map.size(); ++i) {
- object_map->object_map[i] = m_default_object_state;
+ size_t orig_object_map_size = object_map->m_object_map.size();
+ object_map->m_object_map.resize(m_num_objs);
+ for (uint64_t i = orig_object_map_size;
+ i < object_map->m_object_map.size(); ++i) {
+ object_map->m_object_map[i] = m_default_object_state;
}
}
m_new_state, m_current_state);
librados::AioCompletion *rados_completion = create_callback_completion();
- int r = m_image_ctx.data_ctx.aio_operate(object_map_name(m_image_ctx.id),
- rados_completion, &op);
+ std::string oid(object_map_name(m_image_ctx.id, CEPH_NOSNAP));
+ int r = m_image_ctx.md_ctx.aio_operate(oid, rados_completion, &op);
assert(r == 0);
rados_completion->release();
}
ldout(cct, 20) << &m_image_ctx << " updating in-memory object map" << dendl;
for (uint64_t object_no = m_start_object_no;
- object_no < MIN(m_end_object_no, object_map->object_map.size());
+ object_no < MIN(m_end_object_no, object_map->m_object_map.size());
++object_no) {
if (!m_current_state ||
- object_map->object_map[object_no] == *m_current_state) {
- object_map->object_map[object_no] = m_new_state;
+ object_map->m_object_map[object_no] == *m_current_state) {
+ object_map->m_object_map[object_no] = m_new_state;
}
}
}
return image_name + RBD_SUFFIX;
}
- const string object_map_name(const string &image_id)
- {
- return RBD_OBJECT_MAP_PREFIX + image_id;
- }
-
int detect_format(IoCtx &io_ctx, const string &name,
bool *old_format, uint64_t *size)
{
rollback_object(ictx, snap_id, ictx->get_object_name(i), throttle);
prog_ctx.update_progress(i * bsize, numseg * bsize);
}
- rollback_object(ictx, snap_id, object_map_name(ictx->id), throttle);
r = throttle.wait_for_ret();
if (r < 0) {
<< cpp_strerror(r) << dendl;
return r;
}
+
+ {
+ RWLock::RLocker l(ictx->md_lock);
+ if (ictx->object_map != NULL) {
+ ictx->object_map->rollback(snap_id);
+ }
+ }
return 0;
}
if (r < 0)
return r;
+ bool lock_owner = false;
while (ictx->image_watcher->is_lock_supported()) {
r = prepare_image_update(ictx);
if (r < 0) {
return -EROFS;
} else if (ictx->image_watcher->is_lock_owner()) {
+ lock_owner = true;
break;
}
ldout(ictx->cct, 5) << "snap_create timed out notifying lock owner" << dendl;
}
- RWLock::RLocker l2(ictx->md_lock);
+ RWLock::WLocker l2(ictx->md_lock);
+ r = _flush(ictx);
+ if (r < 0) {
+ return r;
+ }
+
do {
- r = add_snap(ictx, snap_name);
+ r = add_snap(ictx, snap_name, lock_owner);
} while (r == -ESTALE);
- if (r < 0)
+ if (r < 0) {
return r;
+ }
if (notify) {
notify_change(ictx->md_ctx, ictx->header_oid, ictx);
}
}
+ if (ictx->object_map != NULL) {
+ r = ictx->md_ctx.remove(ObjectMap::object_map_name(ictx->id, snap_id));
+ if (r < 0 && r != -ENOENT) {
+ lderr(ictx->cct) << "snap_remove: failed to remove snapshot object map"
+ << dendl;
+ return 0;
+ }
+ }
+
r = rm_snap(ictx, snap_name);
if (r < 0)
return r;
r = ictx->data_ctx.selfmanaged_snap_remove(snap_id);
-
if (r < 0)
return r;
librados::ObjectWriteOperation op;
cls_client::object_map_resize(&op, Striper::get_num_objects(layout, size),
OBJECT_NONEXISTENT);
- r = io_ctx.operate(object_map_name(id), &op);
+ r = io_ctx.operate(ObjectMap::object_map_name(id, CEPH_NOSNAP), &op);
if (r < 0) {
goto err_remove_header;
}
}
}
if (!old_format) {
- r = io_ctx.remove(object_map_name(id));
+ r = io_ctx.remove(ObjectMap::object_map_name(id, CEPH_NOSNAP));
if (r < 0 && r != -ENOENT) {
lderr(cct) << "error removing image object map" << dendl;
}
}
- int add_snap(ImageCtx *ictx, const char *snap_name)
+ int add_snap(ImageCtx *ictx, const char *snap_name, bool lock_owner)
{
assert(ictx->owner_lock.is_locked());
+ assert(ictx->md_lock.is_wlocked());
uint64_t snap_id;
int r = ictx->md_ctx.selfmanaged_snap_create(&snap_id);
return r;
}
+ if (!ictx->old_format) {
+ if (ictx->object_map != NULL) {
+ ictx->object_map->snapshot(snap_id);
+ }
+ if (lock_owner) {
+ // immediately start using the new snap context if we
+ // own the exclusive lock
+ std::vector<snapid_t> snaps;
+ snaps.push_back(snap_id);
+ snaps.insert(snaps.end(), ictx->snapc.snaps.begin(),
+ ictx->snapc.snaps.end());
+
+ ictx->snapc.seq = snap_id;
+ ictx->snapc.snaps.swap(snaps);
+ ictx->data_ctx.selfmanaged_snap_set_write_ctx(ictx->snapc.seq,
+ ictx->snaps);
+ }
+ }
return 0;
}
} else {
ictx->object_map = new ObjectMap(*ictx);
if (ictx->snap_exists) {
- ictx->object_map->refresh();
+ ictx->object_map->refresh(ictx->snap_id);
}
}