librbd: rebuilding object map shouldn't update piecemeal

The object map is now rebuilt in-memory and written back to the
OSDs as a single operation.

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
This commit is contained in:
Jason Dillaman 2015-04-10 15:42:58 -04:00
parent 3ec68db139
commit 58be49e592
4 changed files with 85 additions and 66 deletions

View File

@ -33,6 +33,13 @@ std::string ObjectMap::object_map_name(const std::string &image_id,
return oid;
}
ceph::BitVector<2u>::Reference ObjectMap::operator[](uint64_t object_no)
{
assert(m_image_ctx.object_map_lock.is_wlocked());
assert(object_no < m_object_map.size());
return m_object_map[object_no];
}
uint8_t ObjectMap::operator[](uint64_t object_no) const
{
assert(m_image_ctx.object_map_lock.is_locked());
@ -303,6 +310,26 @@ void ObjectMap::snapshot(uint64_t snap_id) {
}
}
void ObjectMap::aio_save(Context *on_finish)
{
assert(m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP));
assert(m_image_ctx.owner_lock.is_locked());
RWLock::RLocker object_map_locker(m_image_ctx.object_map_lock);
librados::ObjectWriteOperation op;
if (m_snap_id == CEPH_NOSNAP) {
rados::cls::lock::assert_locked(&op, RBD_LOCK_NAME, LOCK_EXCLUSIVE, "", "");
}
cls_client::object_map_save(&op, m_object_map);
std::string oid(object_map_name(m_image_ctx.id, m_snap_id));
librados::AioCompletion *comp = librados::Rados::aio_create_completion(
on_finish, NULL, rados_ctx_cb);
int r = m_image_ctx.md_ctx.aio_operate(oid, comp, &op);
assert(r == 0);
}
void ObjectMap::aio_resize(uint64_t new_size, uint8_t default_object_state,
Context *on_finish) {
assert(m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP));

View File

@ -27,6 +27,7 @@ public:
static std::string object_map_name(const std::string &image_id,
uint64_t snap_id);
ceph::BitVector<2u>::Reference operator[](uint64_t object_no);
uint8_t operator[](uint64_t object_no) const;
inline uint64_t size() const {
return m_object_map.size();
@ -37,6 +38,7 @@ public:
bool object_may_exist(uint64_t object_no) const;
void aio_save(Context *on_finish);
void aio_resize(uint64_t new_size, uint8_t default_object_state,
Context *on_finish);
bool aio_update(uint64_t object_no, uint8_t new_state,

View File

@ -25,16 +25,16 @@ namespace {
class C_VerifyObject : public C_AsyncObjectThrottle {
public:
C_VerifyObject(AsyncObjectThrottle &throttle, ImageCtx *image_ctx,
uint64_t snap_id, uint64_t object_no)
uint64_t object_no)
: C_AsyncObjectThrottle(throttle), m_image_ctx(*image_ctx),
m_snap_id(snap_id), m_object_no(object_no),
m_oid(m_image_ctx.get_object_name(m_object_no))
m_object_no(object_no), m_oid(m_image_ctx.get_object_name(m_object_no))
{
}
virtual void complete(int r) {
if (should_complete(r)) {
ldout(m_image_ctx.cct, 5) << " C_VerifyObject completed " << dendl;
ldout(m_image_ctx.cct, 20) << m_oid << " C_VerifyObject completed "
<< dendl;
finish(r);
delete this;
}
@ -46,70 +46,28 @@ public:
}
private:
/**
* Verifying the object map for a single object follows the following state
* machine:
*
* <start>
* |
* v
* STATE_ASSERT_EXISTS --------> STATE_UPDATE_OBJECT_MAP
* . |
* . v
* . . . . . . . . . . . . . . . . > <finish>
*
* The _UPDATE_OBJECT_MAP state is skipped if the object map does not
* need to be updated.
*/
enum State {
STATE_ASSERT_EXISTS,
STATE_UPDATE_OBJECT_MAP
};
ImageCtx &m_image_ctx;
uint64_t m_snap_id;
uint64_t m_object_no;
std::string m_oid;
State m_state;
bool should_complete(int r) {
CephContext *cct = m_image_ctx.cct;
ldout(cct, 5) << m_oid << " C_VerifyObject::should_complete: " << " r=" << r
<< dendl;
bool finished = false;
switch (m_state) {
case STATE_ASSERT_EXISTS:
ldout(cct, 5) << "ASSERT_EXISTS" << dendl;
if (r == 0 || r == -ENOENT) {
return send_update_object_map(r == 0);
}
break;
case STATE_UPDATE_OBJECT_MAP:
ldout(cct, 5) << "UPDATE_OBJECT_MAP" << dendl;
finished = true;
break;
default:
assert(false);
break;
}
if (r < 0) {
lderr(cct) << "encountered an error: " << cpp_strerror(r) << dendl;
if (r < 0 && r != -ENOENT) {
lderr(cct) << m_oid << " C_VerifyObject::should_complete: "
<< "encountered an error: " << cpp_strerror(r) << dendl;
return true;
}
return finished;
ldout(cct, 20) << m_oid << " C_VerifyObject::should_complete: " << " r="
<< r << dendl;
return update_object_map(r == 0);
}
void send_assert_exists() {
ldout(m_image_ctx.cct, 5) << m_oid << " C_VerifyObject::assert_exists"
ldout(m_image_ctx.cct, 5) << m_oid << " C_VerifyObject::send_assert_exists"
<< dendl;
m_state = STATE_ASSERT_EXISTS;
librados::AioCompletion *comp = librados::Rados::aio_create_completion(
this, NULL, rados_ctx_cb);
@ -119,7 +77,7 @@ private:
assert(r == 0);
}
bool send_update_object_map(bool exists) {
bool update_object_map(bool exists) {
CephContext *cct = m_image_ctx.cct;
bool lost_exclusive_lock = false;
@ -140,13 +98,10 @@ private:
}
if (new_state != state) {
ldout(cct, 5) << m_oid << " C_VerifyObject::send_update_object_map"
<< dendl;
bool updating = m_image_ctx.object_map.aio_update(m_object_no,
new_state, state,
this);
assert(updating);
return false;
ldout(cct, 15) << m_oid << " C_VerifyObject::update_object_map "
<< static_cast<uint32_t>(state) << "->"
<< static_cast<uint32_t>(new_state) << dendl;
m_image_ctx.object_map[m_object_no] = new_state;
}
}
}
@ -193,10 +148,16 @@ bool RebuildObjectMapRequest::should_complete(int r) {
case STATE_VERIFY_OBJECTS:
ldout(cct, 5) << "VERIFY_OBJECTS" << dendl;
if (r == 0) {
return send_update_header();
return send_save_object_map();
}
break;
case STATE_SAVE_OBJECT_MAP:
ldout(cct, 5) << "SAVE_OBJECT_MAP" << dendl;
if (r == 0) {
return send_update_header();
}
break;
case STATE_UPDATE_HEADER:
ldout(cct, 5) << "UPDATE_HEADER" << dendl;
if (r == 0) {
@ -295,24 +256,48 @@ void RebuildObjectMapRequest::send_verify_objects() {
m_state = STATE_VERIFY_OBJECTS;
ldout(cct, 5) << this << " send_verify_objects" << dendl;
uint64_t snap_id;
uint64_t num_objects;
{
RWLock::RLocker l(m_image_ctx.snap_lock);
snap_id = m_image_ctx.snap_id;
uint64_t snap_id = m_image_ctx.snap_id;
num_objects = Striper::get_num_objects(m_image_ctx.layout,
m_image_ctx.get_image_size(snap_id));
}
AsyncObjectThrottle::ContextFactory context_factory(
boost::lambda::bind(boost::lambda::new_ptr<C_VerifyObject>(),
boost::lambda::_1, &m_image_ctx, snap_id, boost::lambda::_2));
boost::lambda::_1, &m_image_ctx, boost::lambda::_2));
AsyncObjectThrottle *throttle = new AsyncObjectThrottle(
*this, context_factory, create_callback_context(), m_prog_ctx, 0,
num_objects);
throttle->start_ops(cct->_conf->rbd_concurrent_management_ops);
}
bool RebuildObjectMapRequest::send_save_object_map() {
CephContext *cct = m_image_ctx.cct;
bool lost_exclusive_lock = false;
m_state = STATE_SAVE_OBJECT_MAP;
{
RWLock::RLocker l(m_image_ctx.owner_lock);
if (m_image_ctx.image_watcher->is_lock_supported() &&
!m_image_ctx.image_watcher->is_lock_owner()) {
ldout(cct, 1) << "lost exclusive lock during object map save" << dendl;
lost_exclusive_lock = true;
} else {
ldout(cct, 5) << this << " send_save_object_map" << dendl;
m_image_ctx.object_map.aio_save(create_callback_context());
return false;
}
}
if (lost_exclusive_lock) {
complete(-ERESTART);
return false;
}
return true;
}
bool RebuildObjectMapRequest::send_update_header() {
CephContext *cct = m_image_ctx.cct;
bool lost_exclusive_lock = false;

View File

@ -41,6 +41,9 @@ private:
* . . . > STATE_VERIFY_OBJECTS
* |
* v
* STATE_SAVE_OBJECT_MAP
* |
* v
* STATE_UPDATE_HEADER
*
* The _RESIZE_OBJECT_MAP state will be skipped if the object map
@ -51,6 +54,7 @@ private:
STATE_RESIZE_OBJECT_MAP,
STATE_TRIM_IMAGE,
STATE_VERIFY_OBJECTS,
STATE_SAVE_OBJECT_MAP,
STATE_UPDATE_HEADER
};
@ -62,6 +66,7 @@ private:
void send_resize_object_map();
void send_trim_image();
void send_verify_objects();
bool send_save_object_map();
bool send_update_header();
uint64_t get_image_size() const;