1
0
mirror of https://github.com/ceph/ceph synced 2025-03-30 23:40:09 +00:00

Merge pull request from ceph/wip-rbd-rebuild-object-map

rbd: add ability to rebuild corrupt/missing object maps

Reviewed-by: Josh Durgin <jdurgin@redhat.com>
This commit is contained in:
Josh Durgin 2015-04-10 17:56:48 -07:00
commit 458901dfb2
28 changed files with 1195 additions and 288 deletions

View File

@ -252,6 +252,10 @@ Commands
:command:`image-meta remove` [*image-name*] [*key*]
Remove metadata key with the value.
:command:`object-map` rebuild [*image-name*]
Rebuilds an invalid object map for the specified image. An image snapshot can be
specified to rebuild an invalid object map for a snapshot.
:command:`snap` ls [*image-name*]
Dumps the list of snapshots inside a specific image.

View File

@ -95,6 +95,7 @@ cls_method_handle_t h_dir_add_image;
cls_method_handle_t h_dir_remove_image;
cls_method_handle_t h_dir_rename_image;
cls_method_handle_t h_object_map_load;
cls_method_handle_t h_object_map_save;
cls_method_handle_t h_object_map_resize;
cls_method_handle_t h_object_map_update;
cls_method_handle_t h_metadata_set;
@ -1985,6 +1986,9 @@ int object_map_read(cls_method_context_t hctx, BitVector<2> &object_map)
if (r < 0) {
return r;
}
if (size == 0) {
return -ENOENT;
}
bufferlist bl;
r = cls_cxx_read(hctx, 0, size, &bl);
@ -2025,6 +2029,32 @@ int object_map_load(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
return 0;
}
/**
* Save an rbd image's object map
*
* Input:
* @param object map bit vector
*
* Output:
* @returns 0 on success, negative error code on failure
*/
int object_map_save(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
{
BitVector<2> object_map;
try {
bufferlist::iterator iter = in->begin();
::decode(object_map, iter);
} catch (const buffer::error &err) {
return -EINVAL;
}
bufferlist bl;
::encode(object_map, bl);
CLS_LOG(20, "object_map_save: object size=%" PRIu64 ", byte size=%u",
object_map.size(), bl.length());
return cls_cxx_write_full(hctx, &bl);
}
/**
* Resize an rbd image's object map
*
@ -2669,6 +2699,9 @@ void __cls_init()
cls_register_cxx_method(h_class, "object_map_load",
CLS_METHOD_RD,
object_map_load, &h_object_map_load);
cls_register_cxx_method(h_class, "object_map_save",
CLS_METHOD_RD | CLS_METHOD_WR,
object_map_save, &h_object_map_save);
cls_register_cxx_method(h_class, "object_map_resize",
CLS_METHOD_RD | CLS_METHOD_WR,
object_map_resize, &h_object_map_resize);

View File

@ -731,6 +731,17 @@ namespace librbd {
return 0;
}
void object_map_save(librados::ObjectWriteOperation *rados_op,
const ceph::BitVector<2> &object_map)
{
ceph::BitVector<2> object_map_copy(object_map);
object_map_copy.set_crc_enabled(false);
bufferlist in;
::encode(object_map_copy, in);
rados_op->exec("rbd", "object_map_save", in);
}
void object_map_resize(librados::ObjectWriteOperation *rados_op,
uint64_t object_count, uint8_t default_state)
{

View File

@ -122,6 +122,8 @@ namespace librbd {
// operations on the rbd_object_map.$image_id object
int object_map_load(librados::IoCtx *ioctx, const std::string &oid,
ceph::BitVector<2> *object_map);
void object_map_save(librados::ObjectWriteOperation *rados_op,
const ceph::BitVector<2> &object_map);
void object_map_resize(librados::ObjectWriteOperation *rados_op,
uint64_t object_count, uint8_t default_state);
void object_map_update(librados::ObjectWriteOperation *rados_op,

View File

@ -164,6 +164,10 @@ CEPH_RBD_API int rbd_get_flags(rbd_image_t image, uint64_t *flags);
/* exclusive lock feature */
CEPH_RBD_API int rbd_is_exclusive_lock_owner(rbd_image_t image, int *is_owner);
/* object map feature */
CEPH_RBD_API int rbd_rebuild_object_map(rbd_image_t image,
librbd_progress_fn_t cb, void *cbdata);
CEPH_RBD_API int rbd_copy(rbd_image_t image, rados_ioctx_t dest_io_ctx,
const char *destname);
CEPH_RBD_API int rbd_copy2(rbd_image_t src, rbd_image_t dest);

View File

@ -123,6 +123,9 @@ public:
/* exclusive lock feature */
int is_exclusive_lock_owner(bool *is_owner);
/* object map feature */
int rebuild_object_map(ProgressContext &prog_ctx);
int copy(IoCtx& dest_io_ctx, const char *destname);
int copy2(Image& dest);
int copy_with_progress(IoCtx& dest_io_ctx, const char *destname,

View File

@ -453,6 +453,7 @@ namespace librbd {
m_state = LIBRBD_AIO_WRITE_PRE;
FunctionContext *ctx = new FunctionContext(
boost::bind(&AioRequest::complete, this, _1));
RWLock::WLocker object_map_locker(m_ictx->object_map_lock);
if (!m_ictx->object_map.aio_update(m_object_no, new_state,
current_state, ctx)) {
// no object map update required
@ -487,6 +488,7 @@ namespace librbd {
m_state = LIBRBD_AIO_WRITE_POST;
FunctionContext *ctx = new FunctionContext(
boost::bind(&AioRequest::complete, this, _1));
RWLock::WLocker object_map_locker(m_ictx->object_map_lock);
if (!m_ictx->object_map.aio_update(m_object_no, OBJECT_NONEXISTENT,
OBJECT_PENDING, ctx)) {
// no object map update required

View File

@ -173,6 +173,7 @@ void AsyncTrimRequest::send_pre_remove() {
} else {
// flag the objects as pending deletion
Context *ctx = create_callback_context();
RWLock::WLocker object_map_locker(m_image_ctx.object_map_lock);
if (!m_image_ctx.object_map.aio_update(m_delete_start, m_num_objects,
OBJECT_PENDING, OBJECT_EXISTS,
ctx)) {
@ -210,6 +211,7 @@ bool AsyncTrimRequest::send_post_remove() {
} else {
// flag the pending objects as removed
Context *ctx = create_callback_context();
RWLock::WLocker object_map_locker(m_image_ctx.object_map_lock);
if (!m_image_ctx.object_map.aio_update(m_delete_start, m_num_objects,
OBJECT_NONEXISTENT,
OBJECT_PENDING, ctx)) {

View File

@ -187,6 +187,7 @@ namespace librbd {
} else {
m_state = STATE_OBJECT_MAP;
Context *ctx = create_callback_context();
RWLock::WLocker object_map_locker(m_ictx->object_map_lock);
if (!m_ictx->object_map.aio_update(m_object_no, OBJECT_EXISTS,
boost::optional<uint8_t>(), ctx)) {
delete ctx;

View File

@ -480,6 +480,19 @@ int ImageWatcher::notify_snap_create(const std::string &snap_name) {
return notify_lock_owner(bl);
}
int ImageWatcher::notify_rebuild_object_map(uint64_t request_id,
ProgressContext &prog_ctx) {
assert(m_image_ctx.owner_lock.is_locked());
assert(!is_lock_owner());
AsyncRequestId async_request_id(get_client_id(), request_id);
bufferlist bl;
::encode(NotifyMessage(RebuildObjectMapPayload(async_request_id)), bl);
return notify_async_request(async_request_id, bl, prog_ctx);
}
void ImageWatcher::notify_header_update(librados::IoCtx &io_ctx,
const std::string &oid)
{
@ -701,6 +714,33 @@ int ImageWatcher::notify_async_request(const AsyncRequestId &async_request_id,
return ctx.wait();
}
int ImageWatcher::prepare_async_request(const AsyncRequestId& async_request_id,
bool* new_request, Context** ctx,
ProgressContext** prog_ctx) {
if (async_request_id.client_id == get_client_id()) {
return -ERESTART;
} else {
RWLock::WLocker l(m_async_request_lock);
if (m_async_pending.count(async_request_id) == 0) {
m_async_pending.insert(async_request_id);
*new_request = true;
*prog_ctx = new RemoteProgressContext(*this, async_request_id);
*ctx = new RemoteContext(*this, async_request_id, *prog_ctx);
} else {
*new_request = false;
}
}
return 0;
}
void ImageWatcher::cleanup_async_request(const AsyncRequestId& async_request_id,
Context *ctx) {
delete ctx;
RWLock::WLocker l(m_async_request_lock);
m_async_pending.erase(async_request_id);
}
void ImageWatcher::handle_payload(const HeaderUpdatePayload &payload,
bufferlist *out) {
ldout(m_image_ctx.cct, 10) << "image header updated" << dendl;
@ -807,34 +847,19 @@ void ImageWatcher::handle_payload(const FlattenPayload &payload,
RWLock::RLocker l(m_image_ctx.owner_lock);
if (m_lock_owner_state == LOCK_OWNER_STATE_LOCKED) {
int r = 0;
bool new_request = false;
if (payload.async_request_id.client_id == get_client_id()) {
r = -ERESTART;
} else {
RWLock::WLocker l(m_async_request_lock);
if (m_async_pending.count(payload.async_request_id) == 0) {
m_async_pending.insert(payload.async_request_id);
new_request = true;
}
}
bool new_request;
Context *ctx;
ProgressContext *prog_ctx;
int r = prepare_async_request(payload.async_request_id, &new_request,
&ctx, &prog_ctx);
if (new_request) {
RemoteProgressContext *prog_ctx =
new RemoteProgressContext(*this, payload.async_request_id);
RemoteContext *ctx = new RemoteContext(*this, payload.async_request_id,
prog_ctx);
ldout(m_image_ctx.cct, 10) << "remote flatten request: "
<< payload.async_request_id << dendl;
r = librbd::async_flatten(&m_image_ctx, ctx, *prog_ctx);
if (r < 0) {
delete ctx;
lderr(m_image_ctx.cct) << "remove flatten request failed: "
<< cpp_strerror(r) << dendl;
RWLock::WLocker l(m_async_request_lock);
m_async_pending.erase(payload.async_request_id);
cleanup_async_request(payload.async_request_id, ctx);
}
}
@ -846,24 +871,12 @@ void ImageWatcher::handle_payload(const ResizePayload &payload,
bufferlist *out) {
RWLock::RLocker l(m_image_ctx.owner_lock);
if (m_lock_owner_state == LOCK_OWNER_STATE_LOCKED) {
int r = 0;
bool new_request = false;
if (payload.async_request_id.client_id == get_client_id()) {
r = -ERESTART;
} else {
RWLock::WLocker l(m_async_request_lock);
if (m_async_pending.count(payload.async_request_id) == 0) {
m_async_pending.insert(payload.async_request_id);
new_request = true;
}
}
bool new_request;
Context *ctx;
ProgressContext *prog_ctx;
int r = prepare_async_request(payload.async_request_id, &new_request,
&ctx, &prog_ctx);
if (new_request) {
RemoteProgressContext *prog_ctx =
new RemoteProgressContext(*this, payload.async_request_id);
RemoteContext *ctx = new RemoteContext(*this, payload.async_request_id,
prog_ctx);
ldout(m_image_ctx.cct, 10) << "remote resize request: "
<< payload.async_request_id << " "
<< payload.size << dendl;
@ -871,10 +884,7 @@ void ImageWatcher::handle_payload(const ResizePayload &payload,
if (r < 0) {
lderr(m_image_ctx.cct) << "remove resize request failed: "
<< cpp_strerror(r) << dendl;
delete ctx;
RWLock::WLocker l(m_async_request_lock);
m_async_pending.erase(payload.async_request_id);
cleanup_async_request(payload.async_request_id, ctx);
}
}
@ -888,12 +898,36 @@ void ImageWatcher::handle_payload(const SnapCreatePayload &payload,
if (m_lock_owner_state == LOCK_OWNER_STATE_LOCKED) {
ldout(m_image_ctx.cct, 10) << "remote snap_create request: "
<< payload.snap_name << dendl;
int r = librbd::snap_create(&m_image_ctx, payload.snap_name.c_str(), false);
int r = librbd::snap_create_helper(&m_image_ctx, NULL,
payload.snap_name.c_str());
::encode(ResponseMessage(r), *out);
}
}
void ImageWatcher::handle_payload(const RebuildObjectMapPayload& payload,
bufferlist *out) {
RWLock::RLocker l(m_image_ctx.owner_lock);
if (m_lock_owner_state == LOCK_OWNER_STATE_LOCKED) {
bool new_request;
Context *ctx;
ProgressContext *prog_ctx;
int r = prepare_async_request(payload.async_request_id, &new_request,
&ctx, &prog_ctx);
if (new_request) {
ldout(m_image_ctx.cct, 10) << "remote rebuild object map request: "
<< payload.async_request_id << dendl;
r = librbd::async_rebuild_object_map(&m_image_ctx, ctx, *prog_ctx);
if (r < 0) {
lderr(m_image_ctx.cct) << "remove rebuild object map request failed: "
<< cpp_strerror(r) << dendl;
cleanup_async_request(payload.async_request_id, ctx);
}
}
::encode(ResponseMessage(0), *out);
}
}
void ImageWatcher::handle_payload(const UnknownPayload &payload,
bufferlist *out) {
RWLock::RLocker l(m_image_ctx.owner_lock);

View File

@ -49,6 +49,8 @@ namespace librbd {
int notify_resize(uint64_t request_id, uint64_t size,
ProgressContext &prog_ctx);
int notify_snap_create(const std::string &snap_name);
int notify_rebuild_object_map(uint64_t request_id,
ProgressContext &prog_ctx);
static void notify_header_update(librados::IoCtx &io_ctx,
const std::string &oid);
@ -139,7 +141,7 @@ namespace librbd {
public:
RemoteContext(ImageWatcher &image_watcher,
const WatchNotify::AsyncRequestId &id,
RemoteProgressContext *prog_ctx)
ProgressContext *prog_ctx)
: m_image_watcher(image_watcher), m_async_request_id(id),
m_prog_ctx(prog_ctx)
{
@ -154,7 +156,7 @@ namespace librbd {
private:
ImageWatcher &m_image_watcher;
WatchNotify::AsyncRequestId m_async_request_id;
RemoteProgressContext *m_prog_ctx;
ProgressContext *m_prog_ctx;
};
struct HandlePayloadVisitor : public boost::static_visitor<void> {
@ -239,6 +241,12 @@ namespace librbd {
int notify_async_complete(const WatchNotify::AsyncRequestId &id,
int r);
int prepare_async_request(const WatchNotify::AsyncRequestId& id,
bool* new_request, Context** ctx,
ProgressContext** prog_ctx);
void cleanup_async_request(const WatchNotify::AsyncRequestId& id,
Context *ctx);
void handle_payload(const WatchNotify::HeaderUpdatePayload& payload,
bufferlist *out);
void handle_payload(const WatchNotify::AcquiredLockPayload& payload,
@ -257,6 +265,8 @@ namespace librbd {
bufferlist *out);
void handle_payload(const WatchNotify::SnapCreatePayload& payload,
bufferlist *out);
void handle_payload(const WatchNotify::RebuildObjectMapPayload& payload,
bufferlist *out);
void handle_payload(const WatchNotify::UnknownPayload& payload,
bufferlist *out);

View File

@ -20,7 +20,8 @@ librbd_internal_la_SOURCES = \
librbd/ImageWatcher.cc \
librbd/internal.cc \
librbd/LibrbdWriteback.cc \
librbd/ObjectMap.cc
librbd/ObjectMap.cc \
librbd/RebuildObjectMapRequest.cc
noinst_LTLIBRARIES += librbd_internal.la
librbd_api_la_SOURCES = \
@ -64,6 +65,7 @@ noinst_HEADERS += \
librbd/LibrbdWriteback.h \
librbd/ObjectMap.h \
librbd/parent_types.h \
librbd/RebuildObjectMapRequest.h \
librbd/SnapInfo.h \
librbd/TaskFinisher.h \
librbd/WatchNotifyTypes.h

View File

@ -17,7 +17,7 @@
namespace librbd {
ObjectMap::ObjectMap(ImageCtx &image_ctx)
: m_image_ctx(image_ctx), m_enabled(false)
: m_image_ctx(image_ctx), m_snap_id(CEPH_NOSNAP), m_enabled(false)
{
}
@ -33,6 +33,20 @@ 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());
assert(object_no < m_object_map.size());
return m_object_map[object_no];
}
bool ObjectMap::enabled() const
{
RWLock::RLocker l(m_image_ctx.object_map_lock);
@ -135,10 +149,8 @@ bool ObjectMap::object_may_exist(uint64_t object_no) const
if (!m_enabled) {
return true;
}
assert(object_no < m_object_map.size());
bool exists = (m_object_map[object_no] == OBJECT_EXISTS ||
m_object_map[object_no] == OBJECT_PENDING);
uint8_t state = (*this)[object_no];
bool exists = (state == OBJECT_EXISTS || state == OBJECT_PENDING);
ldout(m_image_ctx.cct, 20) << &m_image_ctx << " object_may_exist: "
<< "object_no=" << object_no << " r=" << exists
<< dendl;
@ -149,6 +161,7 @@ void ObjectMap::refresh(uint64_t snap_id)
{
assert(m_image_ctx.snap_lock.is_wlocked());
RWLock::WLocker l(m_image_ctx.object_map_lock);
m_snap_id = snap_id;
if ((m_image_ctx.features & RBD_FEATURE_OBJECT_MAP) == 0 ||
(m_image_ctx.snap_id == snap_id && !m_image_ctx.snap_exists)) {
@ -161,9 +174,31 @@ void ObjectMap::refresh(uint64_t snap_id)
CephContext *cct = m_image_ctx.cct;
ldout(cct, 10) << &m_image_ctx << " refreshing object map" << dendl;
uint64_t num_objs = Striper::get_num_objects(
m_image_ctx.layout, m_image_ctx.get_image_size(snap_id));
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 == -EINVAL) {
// object map is corrupt on-disk -- clear it and properly size it
// so future IO can keep the object map in sync
invalidate(snap_id);
librados::ObjectWriteOperation op;
if (snap_id == CEPH_NOSNAP) {
rados::cls::lock::assert_locked(&op, RBD_LOCK_NAME, LOCK_EXCLUSIVE, "",
"");
}
op.truncate(0);
cls_client::object_map_resize(&op, num_objs, OBJECT_NONEXISTENT);
r = m_image_ctx.md_ctx.operate(oid, &op);
if (r == 0) {
m_object_map.clear();
resize(num_objs, OBJECT_NONEXISTENT);
}
}
if (r < 0) {
lderr(cct) << "error refreshing object map: " << cpp_strerror(r)
<< dendl;
@ -175,12 +210,23 @@ void ObjectMap::refresh(uint64_t snap_id)
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(snap_id));
if (m_object_map.size() < num_objs) {
lderr(cct) << "object map smaller than current object count: "
<< m_object_map.size() << " != " << num_objs << dendl;
invalidate(snap_id);
// correct the size issue so future IO can keep the object map in sync
librados::ObjectWriteOperation op;
if (snap_id == CEPH_NOSNAP) {
rados::cls::lock::assert_locked(&op, RBD_LOCK_NAME, LOCK_EXCLUSIVE, "",
"");
}
cls_client::object_map_resize(&op, num_objs, OBJECT_NONEXISTENT);
r = m_image_ctx.md_ctx.operate(oid, &op);
if (r == 0) {
resize(num_objs, OBJECT_NONEXISTENT);
}
} else if (m_object_map.size() > num_objs) {
// resize op might have been interrupted
ldout(cct, 1) << "object map larger than current object count: "
@ -264,14 +310,35 @@ 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));
assert(m_image_ctx.owner_lock.is_locked());
assert(m_image_ctx.image_watcher->is_lock_owner());
assert(!m_image_ctx.image_watcher->is_lock_supported() ||
m_image_ctx.image_watcher->is_lock_owner());
ResizeRequest *req = new ResizeRequest(
m_image_ctx, new_size, default_object_state, on_finish);
m_image_ctx, m_snap_id, new_size, default_object_state, on_finish);
req->send();
}
@ -290,11 +357,11 @@ bool ObjectMap::aio_update(uint64_t start_object_no, uint64_t end_object_no,
{
assert(m_image_ctx.test_features(RBD_FEATURE_OBJECT_MAP));
assert(m_image_ctx.owner_lock.is_locked());
assert(m_image_ctx.image_watcher->is_lock_owner());
RWLock::WLocker l(m_image_ctx.object_map_lock);
assert(!m_image_ctx.image_watcher->is_lock_supported() ||
m_image_ctx.image_watcher->is_lock_owner());
assert(m_image_ctx.object_map_lock.is_wlocked());
assert(start_object_no < end_object_no);
CephContext *cct = m_image_ctx.cct;
ldout(cct, 20) << &m_image_ctx << " aio_update: start=" << start_object_no
<< ", end=" << end_object_no << ", new_state="
@ -308,9 +375,10 @@ bool ObjectMap::aio_update(uint64_t start_object_no, uint64_t end_object_no,
++object_no) {
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);
UpdateRequest *req = new UpdateRequest(m_image_ctx, m_snap_id,
start_object_no, end_object_no,
new_state, current_state,
on_finish);
req->send();
return true;
}
@ -347,6 +415,15 @@ void ObjectMap::invalidate(uint64_t snap_id) {
}
}
void ObjectMap::resize(uint64_t num_objs, uint8_t defualt_state) {
size_t orig_object_map_size = m_object_map.size();
m_object_map.resize(num_objs);
for (uint64_t i = orig_object_map_size;
i < m_object_map.size(); ++i) {
m_object_map[i] = defualt_state;
}
}
bool ObjectMap::Request::should_complete(int r) {
CephContext *cct = m_image_ctx.cct;
ldout(cct, 20) << &m_image_ctx << " should_complete: r=" << r << dendl;
@ -422,11 +499,13 @@ void ObjectMap::ResizeRequest::send() {
<< m_num_objs << dendl;
librados::ObjectWriteOperation op;
rados::cls::lock::assert_locked(&op, RBD_LOCK_NAME, LOCK_EXCLUSIVE, "", "");
if (m_snap_id == CEPH_NOSNAP) {
rados::cls::lock::assert_locked(&op, RBD_LOCK_NAME, LOCK_EXCLUSIVE, "", "");
}
cls_client::object_map_resize(&op, m_num_objs, m_default_object_state);
librados::AioCompletion *rados_completion = create_callback_completion();
std::string oid(object_map_name(m_image_ctx.id, CEPH_NOSNAP));
std::string oid(object_map_name(m_image_ctx.id, m_snap_id));
int r = m_image_ctx.md_ctx.aio_operate(oid, rados_completion, &op);
assert(r == 0);
rados_completion->release();
@ -437,48 +516,48 @@ void ObjectMap::ResizeRequest::finish(ObjectMap *object_map) {
ldout(cct, 5) << &m_image_ctx << " resizing in-memory object map: "
<< m_num_objs << dendl;
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;
}
object_map->resize(m_num_objs, m_default_object_state);
}
void ObjectMap::UpdateRequest::send() {
assert(m_image_ctx.object_map_lock.is_wlocked());
CephContext *cct = m_image_ctx.cct;
ldout(cct, 20) << &m_image_ctx << " updating on-disk object map: ["
// safe to update in-memory state first without handling rollback since any
// failures will invalidate the object map
ldout(cct, 20) << &m_image_ctx << " updating object map: ["
<< m_start_object_no << "," << m_end_object_no << ") = "
<< (m_current_state ?
stringify(static_cast<uint32_t>(*m_current_state)) : "")
<< "->" << static_cast<uint32_t>(m_new_state)
<< dendl;
ObjectMap& object_map = m_image_ctx.object_map;
for (uint64_t object_no = m_start_object_no;
object_no < MIN(m_end_object_no, object_map.m_object_map.size());
++object_no) {
if (!m_current_state ||
object_map.m_object_map[object_no] == *m_current_state) {
object_map.m_object_map[object_no] = m_new_state;
}
}
librados::ObjectWriteOperation op;
rados::cls::lock::assert_locked(&op, RBD_LOCK_NAME, LOCK_EXCLUSIVE, "", "");
if (m_snap_id == CEPH_NOSNAP) {
rados::cls::lock::assert_locked(&op, RBD_LOCK_NAME, LOCK_EXCLUSIVE, "", "");
}
cls_client::object_map_update(&op, m_start_object_no, m_end_object_no,
m_new_state, m_current_state);
librados::AioCompletion *rados_completion = create_callback_completion();
std::string oid(object_map_name(m_image_ctx.id, CEPH_NOSNAP));
std::string oid(object_map_name(m_image_ctx.id, m_snap_id));
int r = m_image_ctx.md_ctx.aio_operate(oid, rados_completion, &op);
assert(r == 0);
rados_completion->release();
}
void ObjectMap::UpdateRequest::finish(ObjectMap *object_map) {
CephContext *cct = m_image_ctx.cct;
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->m_object_map.size());
++object_no) {
if (!m_current_state ||
object_map->m_object_map[object_no] == *m_current_state) {
object_map->m_object_map[object_no] = m_new_state;
}
}
ldout(m_image_ctx.cct, 20) << &m_image_ctx << " on-disk object map updated"
<< dendl;
}
} // namespace librbd

View File

@ -27,11 +27,18 @@ 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();
}
int lock();
int unlock();
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,
@ -52,19 +59,21 @@ private:
class Request : public AsyncRequest {
public:
Request(ImageCtx &image_ctx, Context *on_finish)
: AsyncRequest(image_ctx, on_finish), m_state(STATE_REQUEST)
Request(ImageCtx &image_ctx, uint64_t snap_id, Context *on_finish)
: AsyncRequest(image_ctx, on_finish), m_snap_id(snap_id),
m_state(STATE_REQUEST)
{
}
protected:
const uint64_t m_snap_id;
virtual bool should_complete(int r);
virtual int filter_return_code(int r) {
// never propagate an error back to the caller
return 0;
}
virtual void finish(ObjectMap *object_map) = 0;
private:
/**
* <start> ---> STATE_REQUEST ---> <finish>
@ -84,10 +93,10 @@ private:
class ResizeRequest : public Request {
public:
ResizeRequest(ImageCtx &image_ctx, uint64_t new_size,
ResizeRequest(ImageCtx &image_ctx, uint64_t snap_id, uint64_t new_size,
uint8_t default_object_state, Context *on_finish)
: Request(image_ctx, on_finish), m_num_objs(0), m_new_size(new_size),
m_default_object_state(default_object_state)
: Request(image_ctx, snap_id, on_finish), m_num_objs(0),
m_new_size(new_size), m_default_object_state(default_object_state)
{
}
@ -102,13 +111,14 @@ private:
class UpdateRequest : public Request {
public:
UpdateRequest(ImageCtx &image_ctx, uint64_t start_object_no,
uint64_t end_object_no, uint8_t new_state,
UpdateRequest(ImageCtx &image_ctx, uint64_t snap_id,
uint64_t start_object_no, uint64_t end_object_no,
uint8_t new_state,
const boost::optional<uint8_t> &current_state,
Context *on_finish)
: Request(image_ctx, on_finish), m_start_object_no(start_object_no),
m_end_object_no(end_object_no), m_new_state(new_state),
m_current_state(current_state)
: Request(image_ctx, snap_id, on_finish),
m_start_object_no(start_object_no), m_end_object_no(end_object_no),
m_new_state(new_state), m_current_state(current_state)
{
}
@ -124,9 +134,11 @@ private:
ImageCtx &m_image_ctx;
ceph::BitVector<2> m_object_map;
uint64_t m_snap_id;
bool m_enabled;
void invalidate(uint64_t snap_id);
void resize(uint64_t num_objs, uint8_t default_state);
};
} // namespace librbd

View File

@ -0,0 +1,352 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
#include "librbd/RebuildObjectMapRequest.h"
#include "common/dout.h"
#include "common/errno.h"
#include "librbd/AsyncObjectThrottle.h"
#include "librbd/AsyncResizeRequest.h"
#include "librbd/AsyncTrimRequest.h"
#include "librbd/ImageCtx.h"
#include "librbd/ImageWatcher.h"
#include "librbd/internal.h"
#include "librbd/ObjectMap.h"
#include <boost/lambda/bind.hpp>
#include <boost/lambda/construct.hpp>
#define dout_subsys ceph_subsys_rbd
#undef dout_prefix
#define dout_prefix *_dout << "librbd::RebuildObjectMapRequest: "
namespace librbd {
namespace {
class C_VerifyObject : public C_AsyncObjectThrottle {
public:
C_VerifyObject(AsyncObjectThrottle &throttle, ImageCtx *image_ctx,
uint64_t object_no)
: C_AsyncObjectThrottle(throttle), m_image_ctx(*image_ctx),
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, 20) << m_oid << " C_VerifyObject completed "
<< dendl;
finish(r);
delete this;
}
}
virtual int send() {
send_assert_exists();
return 0;
}
private:
ImageCtx &m_image_ctx;
uint64_t m_snap_id;
uint64_t m_object_no;
std::string m_oid;
bool should_complete(int r) {
CephContext *cct = m_image_ctx.cct;
if (r < 0 && r != -ENOENT) {
lderr(cct) << m_oid << " C_VerifyObject::should_complete: "
<< "encountered an error: " << cpp_strerror(r) << dendl;
return true;
}
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::send_assert_exists"
<< dendl;
librados::AioCompletion *comp = librados::Rados::aio_create_completion(
this, NULL, rados_ctx_cb);
librados::ObjectReadOperation op;
op.assert_exists();
int r = m_image_ctx.data_ctx.aio_operate(m_oid, comp, &op, NULL);
assert(r == 0);
}
bool update_object_map(bool exists) {
CephContext *cct = m_image_ctx.cct;
bool lost_exclusive_lock = false;
{
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) << m_oid << " lost exclusive lock during verify" << dendl;
lost_exclusive_lock = true;
} else {
RWLock::WLocker l(m_image_ctx.object_map_lock);
uint8_t state = m_image_ctx.object_map[m_object_no];
uint8_t new_state = state;
if (exists && state == OBJECT_NONEXISTENT) {
new_state = OBJECT_EXISTS;
} else if (!exists && state != OBJECT_NONEXISTENT) {
new_state = OBJECT_NONEXISTENT;
}
if (new_state != state) {
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;
}
}
}
if (lost_exclusive_lock) {
complete(-ERESTART);
return false;
}
return true;
}
};
} // anonymous namespace
void RebuildObjectMapRequest::send() {
send_resize_object_map();
}
bool RebuildObjectMapRequest::should_complete(int r) {
CephContext *cct = m_image_ctx.cct;
ldout(cct, 5) << this << " should_complete: " << " r=" << r << dendl;
switch (m_state) {
case STATE_RESIZE_OBJECT_MAP:
ldout(cct, 5) << "RESIZE_OBJECT_MAP" << dendl;
if (r == -ESTALE && !m_attempted_trim) {
// objects are still flagged as in-use -- delete them
m_attempted_trim = true;
send_trim_image();
return false;
} else if (r == 0) {
send_verify_objects();
}
break;
case STATE_TRIM_IMAGE:
ldout(cct, 5) << "TRIM_IMAGE" << dendl;
if (r == 0) {
send_resize_object_map();
}
break;
case STATE_VERIFY_OBJECTS:
ldout(cct, 5) << "VERIFY_OBJECTS" << dendl;
if (r == 0) {
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) {
return true;
}
break;
default:
assert(false);
break;
}
if (r < 0) {
lderr(cct) << "rebuild object map encountered an error: " << cpp_strerror(r)
<< dendl;
return true;
}
return false;
}
void RebuildObjectMapRequest::send_resize_object_map() {
CephContext *cct = m_image_ctx.cct;
bool lost_exclusive_lock = false;
bool skip_resize = true;
m_state = STATE_RESIZE_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 resize" << dendl;
lost_exclusive_lock = true;
} else {
RWLock::RLocker l(m_image_ctx.snap_lock);
uint64_t size = get_image_size();
uint64_t num_objects = Striper::get_num_objects(m_image_ctx.layout, size);
if (m_image_ctx.object_map.size() != num_objects) {
ldout(cct, 5) << this << " send_resize_object_map" << dendl;
m_image_ctx.object_map.aio_resize(num_objects, OBJECT_NONEXISTENT,
create_callback_context());
skip_resize = false;
}
}
}
if (lost_exclusive_lock) {
complete(-ERESTART);
} else if (skip_resize) {
send_verify_objects();
}
}
void RebuildObjectMapRequest::send_trim_image() {
CephContext *cct = m_image_ctx.cct;
bool lost_exclusive_lock = false;
bool skip_trim = true;
m_state = STATE_TRIM_IMAGE;
{
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 trim" << dendl;
lost_exclusive_lock = true;
} else {
ldout(cct, 5) << this << " send_trim_image" << dendl;
uint64_t new_size;
uint64_t orig_size;
{
RWLock::RLocker l(m_image_ctx.snap_lock);
new_size = get_image_size();
orig_size = m_image_ctx.get_object_size() *
m_image_ctx.object_map.size();
}
AsyncTrimRequest *req = new AsyncTrimRequest(m_image_ctx,
create_callback_context(),
orig_size, new_size,
m_prog_ctx);
req->send();
skip_trim = false;
}
}
if (lost_exclusive_lock) {
complete(-ERESTART);
} else if (skip_trim) {
send_resize_object_map();
}
}
void RebuildObjectMapRequest::send_verify_objects() {
CephContext *cct = m_image_ctx.cct;
m_state = STATE_VERIFY_OBJECTS;
ldout(cct, 5) << this << " send_verify_objects" << dendl;
uint64_t num_objects;
{
RWLock::RLocker l(m_image_ctx.snap_lock);
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, 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;
m_state = STATE_UPDATE_HEADER;
{
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 header update" << dendl;
lost_exclusive_lock = true;
} else {
ldout(cct, 5) << this << " send_update_header" << dendl;
librados::ObjectWriteOperation op;
if (m_image_ctx.image_watcher->is_lock_supported()) {
m_image_ctx.image_watcher->assert_header_locked(&op);
}
cls_client::set_flags(&op, m_image_ctx.snap_id, 0,
RBD_FLAG_OBJECT_MAP_INVALID);
int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid,
create_callback_completion(), &op);
assert(r == 0);
RWLock::WLocker snap_locker(m_image_ctx.snap_lock);
m_image_ctx.update_flags(m_image_ctx.snap_id, RBD_FLAG_OBJECT_MAP_INVALID,
false);
return false;
}
}
if (lost_exclusive_lock) {
complete(-ERESTART);
return false;
}
return true;
}
uint64_t RebuildObjectMapRequest::get_image_size() const {
assert(m_image_ctx.snap_lock.is_locked());
if (m_image_ctx.snap_id == CEPH_NOSNAP) {
if (!m_image_ctx.async_resize_reqs.empty()) {
return m_image_ctx.async_resize_reqs.front()->get_image_size();
} else {
return m_image_ctx.size;
}
}
return m_image_ctx.get_image_size(m_image_ctx.snap_id);
}
} // namespace librbd

View File

@ -0,0 +1,78 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
#ifndef CEPH_LIBRBD_REBUILD_OBJECT_MAP_REQUEST_H
#define CEPH_LIBRBD_REBUILD_OBJECT_MAP_REQUEST_H
#include "include/int_types.h"
#include "librbd/AsyncRequest.h"
namespace librbd {
class ImageCtx;
class ProgressContext;
class RebuildObjectMapRequest : public AsyncRequest {
public:
RebuildObjectMapRequest(ImageCtx &image_ctx, Context *on_finish,
ProgressContext &prog_ctx)
: AsyncRequest(image_ctx, on_finish), m_image_ctx(image_ctx),
m_prog_ctx(prog_ctx), m_attempted_trim(false)
{
}
virtual void send();
protected:
virtual bool should_complete(int r);
private:
/**
* Rebuild object map goes through the following state machine to
* verify per-object state:
*
* <start>
* . | . . . . . . . . . .
* . | . .
* . v v .
* . STATE_RESIZE_OBJECT_MAP . . . > STATE_TRIM_IMAGE
* . |
* . v
* . . . > STATE_VERIFY_OBJECTS
* |
* v
* STATE_SAVE_OBJECT_MAP
* |
* v
* STATE_UPDATE_HEADER
*
* The _RESIZE_OBJECT_MAP state will be skipped if the object map
* is appropriately sized for the image. The _TRIM_IMAGE state will
* only be hit if the resize failed due to an in-use object.
*/
enum State {
STATE_RESIZE_OBJECT_MAP,
STATE_TRIM_IMAGE,
STATE_VERIFY_OBJECTS,
STATE_SAVE_OBJECT_MAP,
STATE_UPDATE_HEADER
};
ImageCtx &m_image_ctx;
ProgressContext &m_prog_ctx;
State m_state;
bool m_attempted_trim;
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;
};
} // namespace librbd
#endif // CEPH_LIBRBD_REBUILD_OBJECT_MAP_REQUEST_H

View File

@ -241,6 +241,22 @@ void SnapCreatePayload::dump(Formatter *f) const {
f->dump_string("snap_name", snap_name);
}
void RebuildObjectMapPayload::encode(bufferlist &bl) const {
::encode(static_cast<uint32_t>(NOTIFY_OP_REBUILD_OBJECT_MAP), bl);
::encode(async_request_id, bl);
}
void RebuildObjectMapPayload::decode(__u8 version, bufferlist::iterator &iter) {
::decode(async_request_id, iter);
}
void RebuildObjectMapPayload::dump(Formatter *f) const {
f->dump_string("notify_op", stringify(NOTIFY_OP_REBUILD_OBJECT_MAP));
f->open_object_section("async_request_id");
async_request_id.dump(f);
f->close_section();
}
void UnknownPayload::encode(bufferlist &bl) const {
assert(false);
}
@ -292,6 +308,9 @@ void NotifyMessage::decode(bufferlist::iterator& iter) {
case NOTIFY_OP_SNAP_CREATE:
payload = SnapCreatePayload();
break;
case NOTIFY_OP_REBUILD_OBJECT_MAP:
payload = RebuildObjectMapPayload();
break;
default:
payload = UnknownPayload();
break;
@ -315,6 +334,7 @@ void NotifyMessage::generate_test_instances(std::list<NotifyMessage *> &o) {
o.push_back(new NotifyMessage(FlattenPayload(AsyncRequestId(ClientId(0, 1), 2))));
o.push_back(new NotifyMessage(ResizePayload(123, AsyncRequestId(ClientId(0, 1), 2))));
o.push_back(new NotifyMessage(SnapCreatePayload("foo")));
o.push_back(new NotifyMessage(RebuildObjectMapPayload(AsyncRequestId(ClientId(0, 1), 2))));
}
void ResponseMessage::encode(bufferlist& bl) const {
@ -372,6 +392,9 @@ std::ostream &operator<<(std::ostream &out,
case NOTIFY_OP_SNAP_CREATE:
out << "SnapCreate";
break;
case NOTIFY_OP_REBUILD_OBJECT_MAP:
out << "RebuildObjectMap";
break;
default:
out << "Unknown (" << static_cast<uint32_t>(op) << ")";
break;

View File

@ -73,15 +73,16 @@ struct AsyncRequestId {
};
enum NotifyOp {
NOTIFY_OP_ACQUIRED_LOCK = 0,
NOTIFY_OP_RELEASED_LOCK = 1,
NOTIFY_OP_REQUEST_LOCK = 2,
NOTIFY_OP_HEADER_UPDATE = 3,
NOTIFY_OP_ASYNC_PROGRESS = 4,
NOTIFY_OP_ASYNC_COMPLETE = 5,
NOTIFY_OP_FLATTEN = 6,
NOTIFY_OP_RESIZE = 7,
NOTIFY_OP_SNAP_CREATE = 8
NOTIFY_OP_ACQUIRED_LOCK = 0,
NOTIFY_OP_RELEASED_LOCK = 1,
NOTIFY_OP_REQUEST_LOCK = 2,
NOTIFY_OP_HEADER_UPDATE = 3,
NOTIFY_OP_ASYNC_PROGRESS = 4,
NOTIFY_OP_ASYNC_COMPLETE = 5,
NOTIFY_OP_FLATTEN = 6,
NOTIFY_OP_RESIZE = 7,
NOTIFY_OP_SNAP_CREATE = 8,
NOTIFY_OP_REBUILD_OBJECT_MAP = 9
};
struct AcquiredLockPayload {
@ -179,7 +180,18 @@ struct SnapCreatePayload {
SnapCreatePayload(const std::string &name) : snap_name(name) {}
std::string snap_name;
void encode(bufferlist &bl) const;
void decode(__u8 version, bufferlist::iterator &iter);
void dump(Formatter *f) const;
};
struct RebuildObjectMapPayload {
RebuildObjectMapPayload() {}
RebuildObjectMapPayload(const AsyncRequestId &id) : async_request_id(id) {}
AsyncRequestId async_request_id;
void encode(bufferlist &bl) const;
void decode(__u8 version, bufferlist::iterator &iter);
void dump(Formatter *f) const;
@ -200,6 +212,7 @@ typedef boost::variant<AcquiredLockPayload,
FlattenPayload,
ResizePayload,
SnapCreatePayload,
RebuildObjectMapPayload,
UnknownPayload> Payload;
struct NotifyMessage {

View File

@ -24,10 +24,10 @@
#include "librbd/CopyupRequest.h"
#include "librbd/ImageCtx.h"
#include "librbd/ImageWatcher.h"
#include "librbd/internal.h"
#include "librbd/ObjectMap.h"
#include "librbd/parent_types.h"
#include "librbd/RebuildObjectMapRequest.h"
#include "include/util.h"
#include "librados/snap_set_diff.h"
@ -81,6 +81,81 @@ int remove_object_map(ImageCtx *ictx) {
return 0;
}
int prepare_image_update(ImageCtx *ictx) {
assert(ictx->owner_lock.is_locked() && !ictx->owner_lock.is_wlocked());
if (ictx->image_watcher == NULL) {
return -EROFS;
} else if (!ictx->image_watcher->is_lock_supported() ||
ictx->image_watcher->is_lock_owner()) {
return 0;
}
// need to upgrade to a write lock
int r = 0;
bool acquired_lock = false;
ictx->owner_lock.put_read();
{
RWLock::WLocker l(ictx->owner_lock);
if (!ictx->image_watcher->is_lock_owner()) {
r = ictx->image_watcher->try_lock();
acquired_lock = ictx->image_watcher->is_lock_owner();
}
}
if (acquired_lock) {
// finish any AIO that was previously waiting on acquiring the
// exclusive lock
ictx->flush_async_operations();
}
ictx->owner_lock.get_read();
return r;
}
int invoke_async_request(ImageCtx *ictx, const std::string& request_type,
const boost::function<int(Context*)>& local_request,
const boost::function<int()>& remote_request) {
int r;
do {
C_SaferCond ctx;
{
RWLock::RLocker l(ictx->owner_lock);
{
RWLock::RLocker snap_locker(ictx->snap_lock);
if (ictx->read_only || ictx->snap_id != CEPH_NOSNAP) {
return -EROFS;
}
}
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()) {
break;
}
r = remote_request();
if (r != -ETIMEDOUT && r != -ERESTART) {
return r;
}
ldout(ictx->cct, 5) << request_type << " timed out notifying lock owner"
<< dendl;
}
r = local_request(&ctx);
if (r < 0) {
return r;
}
}
r = ctx.wait();
if (r == -ERESTART) {
ldout(ictx->cct, 5) << request_type << " interrupted: restarting"
<< dendl;
}
} while (r == -ERESTART);
return r;
}
} // anonymous namespace
const string id_obj_name(const string &name)
@ -471,43 +546,13 @@ int remove_object_map(ImageCtx *ictx) {
return 0;
}
static int prepare_image_update(ImageCtx *ictx)
int snap_create(ImageCtx *ictx, const char *snap_name)
{
assert(ictx->owner_lock.is_locked() && !ictx->owner_lock.is_wlocked());
if (ictx->image_watcher == NULL) {
return -EROFS;
} else if (!ictx->image_watcher->is_lock_supported() ||
ictx->image_watcher->is_lock_owner()) {
return 0;
}
// need to upgrade to a write lock
int r = 0;
bool acquired_lock = false;
ictx->owner_lock.put_read();
{
RWLock::WLocker l(ictx->owner_lock);
if (!ictx->image_watcher->is_lock_owner()) {
r = ictx->image_watcher->try_lock();
acquired_lock = ictx->image_watcher->is_lock_owner();
}
}
if (acquired_lock) {
// finish any AIO that was previously waiting on acquiring the
// exclusive lock
ictx->flush_async_operations();
}
ictx->owner_lock.get_read();
return r;
}
int snap_create(ImageCtx *ictx, const char *snap_name, bool notify)
{
assert(ictx->owner_lock.is_locked());
ldout(ictx->cct, 20) << "snap_create " << ictx << " " << snap_name << dendl;
if (ictx->read_only)
if (ictx->read_only) {
return -EROFS;
}
int r = ictx_check(ictx);
if (r < 0)
@ -520,45 +565,51 @@ int remove_object_map(ImageCtx *ictx) {
}
}
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;
}
r = ictx->image_watcher->notify_snap_create(snap_name);
if (r == 0 || r == -EEXIST) {
notify_change(ictx->md_ctx, ictx->header_oid, ictx);
return 0;
} else if (r != -ETIMEDOUT) {
return r;
}
ldout(ictx->cct, 5) << "snap_create timed out notifying lock owner" << dendl;
r = invoke_async_request(ictx, "snap_create",
boost::bind(&snap_create_helper, ictx, _1,
snap_name),
boost::bind(&ImageWatcher::notify_snap_create,
ictx->image_watcher, snap_name));
if (r < 0 && r != -EEXIST) {
return r;
}
RWLock::WLocker l2(ictx->md_lock);
ictx->perfcounter->inc(l_librbd_snap_create);
notify_change(ictx->md_ctx, ictx->header_oid, ictx);
return 0;
}
int snap_create_helper(ImageCtx* ictx, Context* ctx,
const char* snap_name) {
assert(ictx->owner_lock.is_locked());
assert(!ictx->image_watcher->is_lock_supported() ||
ictx->image_watcher->is_lock_owner());
ldout(ictx->cct, 20) << "snap_create_helper " << ictx << " " << snap_name
<< dendl;
int r = ictx_check(ictx);
if (r < 0) {
return r;
}
RWLock::WLocker md_locker(ictx->md_lock);
r = _flush(ictx);
if (r < 0) {
return r;
}
do {
r = add_snap(ictx, snap_name, lock_owner);
r = add_snap(ictx, snap_name);
} while (r == -ESTALE);
if (r < 0) {
return r;
}
if (notify) {
notify_change(ictx->md_ctx, ictx->header_oid, ictx);
if (ctx != NULL) {
ctx->complete(0);
}
ictx->perfcounter->inc(l_librbd_snap_create);
return 0;
}
@ -1758,42 +1809,12 @@ reprotect_and_return_err:
}
uint64_t request_id = ictx->async_request_seq.inc();
do {
C_SaferCond ctx;
{
RWLock::RLocker l(ictx->owner_lock);
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()) {
break;
}
RWLock::RLocker snap_locker(ictx->snap_lock);
if (ictx->snap_id != CEPH_NOSNAP || ictx->read_only) {
return -EROFS;
}
r = ictx->image_watcher->notify_resize(request_id, size, prog_ctx);
if (r != -ETIMEDOUT && r != -ERESTART) {
return r;
}
ldout(ictx->cct, 5) << "resize timed out notifying lock owner"
<< dendl;
}
r = async_resize(ictx, &ctx, size, prog_ctx);
if (r < 0) {
return r;
}
}
r = ctx.wait();
if (r == -ERESTART) {
ldout(ictx->cct, 5) << "resize interrupted: restarting" << dendl;
}
} while (r == -ERESTART);
r = invoke_async_request(ictx, "resize",
boost::bind(&async_resize, ictx, _1, size,
boost::ref(prog_ctx)),
boost::bind(&ImageWatcher::notify_resize,
ictx->image_watcher, request_id, size,
boost::ref(prog_ctx)));
ictx->perfcounter->inc(l_librbd_resize);
notify_change(ictx->md_ctx, ictx->header_oid, ictx);
@ -1874,12 +1895,17 @@ reprotect_and_return_err:
}
int add_snap(ImageCtx *ictx, const char *snap_name, bool lock_owner)
int add_snap(ImageCtx *ictx, const char *snap_name)
{
assert(ictx->owner_lock.is_locked());
assert(ictx->md_lock.is_wlocked());
uint64_t snap_id;
bool lock_owner = ictx->image_watcher->is_lock_owner();
if (ictx->image_watcher->is_lock_supported()) {
assert(lock_owner);
}
uint64_t snap_id;
int r = ictx->md_ctx.selfmanaged_snap_create(&snap_id);
if (r < 0) {
lderr(ictx->cct) << "failed to create snap id: " << cpp_strerror(-r)
@ -1892,7 +1918,7 @@ reprotect_and_return_err:
snap_id, snap_name);
} else {
librados::ObjectWriteOperation op;
if (ictx->image_watcher->is_lock_supported()) {
if (lock_owner) {
ictx->image_watcher->assert_header_locked(&op);
}
cls_client::snapshot_add(&op, snap_id, snap_name);
@ -2602,45 +2628,13 @@ reprotect_and_return_err:
return r;
}
{
RWLock::RLocker snap_locker(ictx->snap_lock);
if (ictx->read_only || ictx->snap_id != CEPH_NOSNAP) {
return -EROFS;
}
}
uint64_t request_id = ictx->async_request_seq.inc();
do {
C_SaferCond ctx;
{
RWLock::RLocker l(ictx->owner_lock);
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()) {
break;
}
r = ictx->image_watcher->notify_flatten(request_id, prog_ctx);
if (r != -ETIMEDOUT && r != -ERESTART) {
return r;
}
ldout(ictx->cct, 5) << "flatten timed out notifying lock owner"
<< dendl;
}
r = async_flatten(ictx, &ctx, prog_ctx);
if (r < 0) {
return r;
}
}
r = ctx.wait();
if (r == -ERESTART) {
ldout(ictx->cct, 5) << "flatten interrupted: restarting" << dendl;
}
} while (r == -ERESTART);
r = invoke_async_request(ictx, "flatten",
boost::bind(&async_flatten, ictx, _1,
boost::ref(prog_ctx)),
boost::bind(&ImageWatcher::notify_flatten,
ictx->image_watcher, request_id,
boost::ref(prog_ctx)));
notify_change(ictx->md_ctx, ictx->header_oid, ictx);
ldout(cct, 20) << "flatten finished" << dendl;
@ -2672,7 +2666,7 @@ reprotect_and_return_err:
RWLock::RLocker l(ictx->snap_lock);
RWLock::RLocker l2(ictx->parent_lock);
if (ictx->read_only || ictx->snap_id != CEPH_NOSNAP) {
if (ictx->read_only) {
return -EROFS;
}
@ -2703,6 +2697,77 @@ reprotect_and_return_err:
return 0;
}
int rebuild_object_map(ImageCtx *ictx, ProgressContext &prog_ctx) {
CephContext *cct = ictx->cct;
ldout(cct, 10) << "rebuild_object_map" << dendl;
int r = ictx_check(ictx);
if (r < 0) {
return r;
}
uint64_t snap_id;
{
RWLock::RLocker snap_locker(ictx->snap_lock);
snap_id = ictx->snap_id;
}
if (snap_id == CEPH_NOSNAP) {
uint64_t request_id = ictx->async_request_seq.inc();
r = invoke_async_request(ictx, "rebuild object map",
boost::bind(&async_rebuild_object_map, ictx, _1,
boost::ref(prog_ctx)),
boost::bind(&ImageWatcher::notify_rebuild_object_map,
ictx->image_watcher, request_id,
boost::ref(prog_ctx)));
} else {
C_SaferCond ctx;
{
RWLock::RLocker owner_locker(ictx->owner_lock);
r = async_rebuild_object_map(ictx, &ctx, prog_ctx);
}
if (r == 0) {
r = ctx.wait();
}
}
ldout(cct, 10) << "rebuild object map finished" << dendl;
if (r < 0) {
notify_change(ictx->md_ctx, ictx->header_oid, ictx);
}
return r;
}
int async_rebuild_object_map(ImageCtx *ictx, Context *ctx,
ProgressContext &prog_ctx) {
assert(ictx->owner_lock.is_locked());
assert(!ictx->image_watcher->is_lock_supported() ||
ictx->image_watcher->is_lock_owner());
CephContext *cct = ictx->cct;
ldout(cct, 20) << "async_rebuild_object_map " << ictx << dendl;
int r = ictx_check(ictx);
if (r < 0) {
return r;
}
{
RWLock::RLocker snap_locker(ictx->snap_lock);
if (ictx->read_only) {
return -EROFS;
}
if (!ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
return -EINVAL;
}
}
RebuildObjectMapRequest *req = new RebuildObjectMapRequest(*ictx, ctx,
prog_ctx);
req->send();
return 0;
}
int list_lockers(ImageCtx *ictx,
std::list<locker_t> *lockers,
bool *exclusive,

View File

@ -110,7 +110,8 @@ namespace librbd {
int remove(librados::IoCtx& io_ctx, const char *imgname,
ProgressContext& prog_ctx);
int resize(ImageCtx *ictx, uint64_t size, ProgressContext& prog_ctx);
int snap_create(ImageCtx *ictx, const char *snap_name, bool notify);
int snap_create(ImageCtx *ictx, const char *snap_name);
int snap_create_helper(ImageCtx *ictx, Context* ctx, const char *snap_name);
int snap_list(ImageCtx *ictx, std::vector<snap_info_t>& snaps);
bool snap_exists(ImageCtx *ictx, const char *snap_name);
int snap_rollback(ImageCtx *ictx, const char *snap_name,
@ -120,7 +121,7 @@ namespace librbd {
int snap_unprotect(ImageCtx *ictx, const char *snap_name);
int snap_is_protected(ImageCtx *ictx, const char *snap_name,
bool *is_protected);
int add_snap(ImageCtx *ictx, const char *snap_name, bool lock_owner);
int add_snap(ImageCtx *ictx, const char *snap_name);
int rm_snap(ImageCtx *ictx, const char *snap_name);
int refresh_parent(ImageCtx *ictx);
int ictx_check(ImageCtx *ictx);
@ -137,6 +138,8 @@ namespace librbd {
const char *buf);
int flatten(ImageCtx *ictx, ProgressContext &prog_ctx);
int rebuild_object_map(ImageCtx *ictx, ProgressContext &prog_ctx);
/* cooperative locking */
int list_lockers(ImageCtx *ictx,
std::list<locker_t> *locks,
@ -196,6 +199,8 @@ namespace librbd {
ProgressContext &prog_ctx);
void async_resize_helper(ImageCtx *ictx, Context *ctx, uint64_t new_size,
ProgressContext& prog_ctx);
int async_rebuild_object_map(ImageCtx *ictx, Context *ctx,
ProgressContext &prog_ctx);
int aio_write(ImageCtx *ictx, uint64_t off, size_t len, const char *buf,
AioCompletion *c, int op_flags);

View File

@ -387,6 +387,12 @@ namespace librbd {
return r;
}
int Image::rebuild_object_map(ProgressContext &prog_ctx)
{
ImageCtx *ictx = reinterpret_cast<ImageCtx*>(ctx);
return librbd::rebuild_object_map(ictx, prog_ctx);
}
int Image::copy(IoCtx& dest_io_ctx, const char *destname)
{
ImageCtx *ictx = (ImageCtx *)ctx;
@ -518,8 +524,7 @@ namespace librbd {
{
ImageCtx *ictx = (ImageCtx *)ctx;
tracepoint(librbd, snap_create_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, snap_name);
RWLock::RLocker owner_locker(ictx->owner_lock);
int r = librbd::snap_create(ictx, snap_name, true);
int r = librbd::snap_create(ictx, snap_name);
tracepoint(librbd, snap_create_exit, r);
return r;
}
@ -1269,13 +1274,20 @@ extern "C" int rbd_is_exclusive_lock_owner(rbd_image_t image, int *is_owner)
return r;
}
extern "C" int rbd_rebuild_object_map(rbd_image_t image,
librbd_progress_fn_t cb, void *cbdata)
{
librbd::ImageCtx *ictx = reinterpret_cast<librbd::ImageCtx*>(image);
librbd::CProgressContext prog_ctx(cb, cbdata);
return librbd::rebuild_object_map(ictx, prog_ctx);
}
/* snapshots */
extern "C" int rbd_snap_create(rbd_image_t image, const char *snap_name)
{
librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
tracepoint(librbd, snap_create_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, snap_name);
RWLock::RLocker owner_locker(ictx->owner_lock);
int r = librbd::snap_create(ictx, snap_name, true);
int r = librbd::snap_create(ictx, snap_name);
tracepoint(librbd, snap_create_exit, r);
return r;
}

View File

@ -125,6 +125,7 @@ void usage()
" image-meta get <image-name> <key> image metadata get the value associated with the key\n"
" image-meta set <image-name> <key> <value> image metadata set key with value\n"
" image-meta remove <image-name> <key> image metadata remove the key and value associated\n"
" object-map rebuild <image-name> rebuild an invalid object map\n"
" snap ls <image-name> dump list of image snapshots\n"
" snap create <snap-name> create a snapshot\n"
" snap rollback <snap-name> rollback image to snapshot\n"
@ -2358,6 +2359,18 @@ static int do_show_status(librados::IoCtx &io_ctx, librbd::Image &image,
return 0;
}
static int do_object_map_rebuild(librbd::Image &image)
{
MyProgressContext pc("Object Map Rebuild");
int r = image.rebuild_object_map(pc);
if (r < 0) {
pc.fail();
return r;
}
pc.finish();
return 0;
}
static int do_kernel_map(const char *poolname, const char *imgname,
const char *snapname)
{
@ -2533,7 +2546,8 @@ enum CommandType{
COMMAND_TYPE_SNAP,
COMMAND_TYPE_LOCK,
COMMAND_TYPE_METADATA,
COMMAND_TYPE_FEATURE
COMMAND_TYPE_FEATURE,
COMMAND_TYPE_OBJECT_MAP
};
enum {
@ -2576,6 +2590,7 @@ enum {
OPT_METADATA_SET,
OPT_METADATA_GET,
OPT_METADATA_REMOVE,
OPT_OBJECT_MAP_REBUILD
};
static int get_cmd(const char *cmd, CommandType command_type)
@ -2678,6 +2693,10 @@ static int get_cmd(const char *cmd, CommandType command_type)
return OPT_FEATURE_ENABLE;
}
break;
case COMMAND_TYPE_OBJECT_MAP:
if (strcmp(cmd, "rebuild") == 0)
return OPT_OBJECT_MAP_REBUILD;
break;
}
return OPT_NO_CMD;
@ -2890,43 +2909,31 @@ int main(int argc, const char **argv)
common_init_finish(g_ceph_context);
std::map<std::string, CommandType> command_map = boost::assign::map_list_of
("snap", COMMAND_TYPE_SNAP)
("lock", COMMAND_TYPE_LOCK)
("image-meta", COMMAND_TYPE_METADATA)
("feature", COMMAND_TYPE_FEATURE)
("object-map", COMMAND_TYPE_OBJECT_MAP);
i = args.begin();
if (i == args.end()) {
cerr << "rbd: you must specify a command." << std::endl;
return EXIT_FAILURE;
} else if (strcmp(*i, "snap") == 0) {
} else if (command_map.count(*i) > 0) {
std::string command(*i);
i = args.erase(i);
if (i == args.end()) {
cerr << "rbd: which snap command do you want?" << std::endl;
cerr << "rbd: which " << command << " command do you want?" << std::endl;
return EXIT_FAILURE;
}
opt_cmd = get_cmd(*i, COMMAND_TYPE_SNAP);
} else if (strcmp(*i, "lock") == 0) {
i = args.erase(i);
if (i == args.end()) {
cerr << "rbd: which lock command do you want?" << std::endl;
return EXIT_FAILURE;
}
opt_cmd = get_cmd(*i, COMMAND_TYPE_LOCK);
} else if (strcmp(*i, "image-meta") == 0) {
i = args.erase(i);
if (i == args.end()) {
cerr << "rbd: which image-meta command do you want?" << std::endl;
return EXIT_FAILURE;
}
opt_cmd = get_cmd(*i, COMMAND_TYPE_METADATA);
} else if (strcmp(*i, "feature") == 0) {
i = args.erase(i);
if (i == args.end()) {
cerr << "rbd: which feature command do you want?" << std::endl;
return EXIT_FAILURE;
}
opt_cmd = get_cmd(*i, COMMAND_TYPE_FEATURE);
opt_cmd = get_cmd(*i, command_map[command]);
} else {
opt_cmd = get_cmd(*i, COMMAND_TYPE_NONE);
}
if (opt_cmd == OPT_NO_CMD) {
cerr << "rbd: error parsing command '" << *i << "'; -h or --help for usage" << std::endl;
cerr << "rbd: error parsing command '" << *i << "'; -h or --help for usage"
<< std::endl;
return EXIT_FAILURE;
}
@ -2966,6 +2973,7 @@ if (!set_conf_param(v, p1, p2, p3)) { \
case OPT_LOCK_LIST:
case OPT_METADATA_LIST:
case OPT_DIFF:
case OPT_OBJECT_MAP_REBUILD:
SET_CONF_PARAM(v, &imgname, NULL, NULL);
break;
case OPT_UNMAP:
@ -3147,10 +3155,11 @@ if (!set_conf_param(v, p1, p2, p3)) { \
(char **)&imgname, (char **)&snapname);
if (snapname && opt_cmd != OPT_SNAP_CREATE && opt_cmd != OPT_SNAP_ROLLBACK &&
opt_cmd != OPT_SNAP_REMOVE && opt_cmd != OPT_INFO &&
opt_cmd != OPT_EXPORT && opt_cmd != OPT_EXPORT_DIFF && opt_cmd != OPT_DIFF && opt_cmd != OPT_COPY &&
opt_cmd != OPT_EXPORT && opt_cmd != OPT_EXPORT_DIFF &&
opt_cmd != OPT_DIFF && opt_cmd != OPT_COPY &&
opt_cmd != OPT_MAP && opt_cmd != OPT_CLONE &&
opt_cmd != OPT_SNAP_PROTECT && opt_cmd != OPT_SNAP_UNPROTECT &&
opt_cmd != OPT_CHILDREN) {
opt_cmd != OPT_CHILDREN && opt_cmd != OPT_OBJECT_MAP_REBUILD) {
cerr << "rbd: snapname specified for a command that doesn't use it"
<< std::endl;
return EXIT_FAILURE;
@ -3263,7 +3272,8 @@ if (!set_conf_param(v, p1, p2, p3)) { \
opt_cmd == OPT_CHILDREN || opt_cmd == OPT_LOCK_LIST ||
opt_cmd == OPT_METADATA_SET || opt_cmd == OPT_METADATA_LIST ||
opt_cmd == OPT_METADATA_REMOVE || opt_cmd == OPT_METADATA_GET ||
opt_cmd == OPT_FEATURE_DISABLE || opt_cmd == OPT_FEATURE_ENABLE)) {
opt_cmd == OPT_FEATURE_DISABLE || opt_cmd == OPT_FEATURE_ENABLE ||
opt_cmd == OPT_OBJECT_MAP_REBUILD)) {
if (opt_cmd == OPT_INFO || opt_cmd == OPT_SNAP_LIST ||
opt_cmd == OPT_EXPORT || opt_cmd == OPT_EXPORT || opt_cmd == OPT_COPY ||
@ -3287,7 +3297,8 @@ if (!set_conf_param(v, p1, p2, p3)) { \
opt_cmd == OPT_EXPORT_DIFF ||
opt_cmd == OPT_DIFF ||
opt_cmd == OPT_COPY ||
opt_cmd == OPT_CHILDREN)) {
opt_cmd == OPT_CHILDREN ||
opt_cmd == OPT_OBJECT_MAP_REBUILD)) {
r = image.snap_set(snapname);
if (r < 0) {
cerr << "rbd: error setting snapshot context: " << cpp_strerror(-r)
@ -3713,6 +3724,15 @@ if (!set_conf_param(v, p1, p2, p3)) { \
return -r;
}
break;
case OPT_OBJECT_MAP_REBUILD:
r = do_object_map_rebuild(image);
if (r < 0) {
cerr << "rbd: rebuilding object map failed: " << cpp_strerror(r)
<< std::endl;
return -r;
}
break;
}
return 0;
}

View File

@ -38,6 +38,7 @@
image-meta get <image-name> <key> image metadata get the value associated with the key
image-meta set <image-name> <key> <value> image metadata set key with value
image-meta remove <image-name> <key> image metadata remove the key and value associated
object-map rebuild <image-name> rebuild an invalid object map
snap ls <image-name> dump list of image snapshots
snap create <snap-name> create a snapshot
snap rollback <snap-name> rollback image to snapshot

View File

@ -51,6 +51,7 @@ using ::librbd::cls_client::set_stripe_unit_count;
using ::librbd::cls_client::old_snapshot_add;
using ::librbd::cls_client::get_mutable_metadata;
using ::librbd::cls_client::object_map_load;
using ::librbd::cls_client::object_map_save;
using ::librbd::cls_client::object_map_resize;
using ::librbd::cls_client::object_map_update;
using ::librbd::cls_client::get_flags;
@ -912,6 +913,27 @@ TEST_F(TestClsRbd, get_mutable_metadata_features)
ioctx.close();
}
TEST_F(TestClsRbd, object_map_save)
{
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
string oid = get_temp_image_name();
BitVector<2> ref_bit_vector;
ref_bit_vector.resize(32);
for (uint64_t i = 0; i < ref_bit_vector.size(); ++i) {
ref_bit_vector[i] = 1;
}
librados::ObjectWriteOperation op;
object_map_save(&op, ref_bit_vector);
ASSERT_EQ(0, ioctx.operate(oid, &op));
BitVector<2> osd_bit_vector;
ASSERT_EQ(0, object_map_load(&ioctx, oid, &osd_bit_vector));
ASSERT_EQ(ref_bit_vector, osd_bit_vector);
}
TEST_F(TestClsRbd, object_map_resize)
{
librados::IoCtx ioctx;

View File

@ -317,6 +317,11 @@ int IoCtx::aio_flush_async(AioCompletion *c) {
return 0;
}
int IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
ObjectReadOperation *op, bufferlist *pbl) {
return aio_operate(oid, c, op, 0, pbl);
}
int IoCtx::aio_operate(const std::string& oid, AioCompletion *c,
ObjectReadOperation *op, int flags,
bufferlist *pbl) {

View File

@ -230,6 +230,13 @@ public:
*id = payload.async_request_id;
}
return true;
case NOTIFY_OP_REBUILD_OBJECT_MAP:
{
RebuildObjectMapPayload payload;
payload.decode(2, iter);
*id = payload.async_request_id;
}
return true;
default:
break;
}
@ -329,6 +336,20 @@ struct ResizeTask {
}
};
struct RebuildObjectMapTask {
librbd::ImageCtx *ictx;
ProgressContext *progress_context;
int result;
RebuildObjectMapTask(librbd::ImageCtx *ictx_, ProgressContext *ctx)
: ictx(ictx_), progress_context(ctx), result(0) {}
void operator()() {
RWLock::RLocker l(ictx->owner_lock);
result = ictx->image_watcher->notify_rebuild_object_map(0, *progress_context);
}
};
TEST_F(TestImageWatcher, IsLockSupported) {
REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
@ -781,6 +802,42 @@ TEST_F(TestImageWatcher, NotifyResize) {
ASSERT_EQ(0, resize_task.result);
}
TEST_F(TestImageWatcher, NotifyRebuildObjectMap) {
REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, register_image_watch(*ictx));
ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
"auto " + stringify(m_watch_ctx->get_handle())));
m_notify_acks = boost::assign::list_of(
std::make_pair(NOTIFY_OP_REBUILD_OBJECT_MAP, create_response_message(0)));
ProgressContext progress_context;
RebuildObjectMapTask rebuild_task(ictx, &progress_context);
boost::thread thread(boost::ref(rebuild_task));
ASSERT_TRUE(wait_for_notifies(*ictx));
NotifyOps expected_notify_ops;
expected_notify_ops += NOTIFY_OP_REBUILD_OBJECT_MAP;
ASSERT_EQ(expected_notify_ops, m_notifies);
AsyncRequestId async_request_id;
ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_REBUILD_OBJECT_MAP,
&async_request_id));
ASSERT_EQ(0, notify_async_progress(ictx, async_request_id, 10, 20));
ASSERT_TRUE(progress_context.wait(ictx, 10, 20));
ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
ASSERT_EQ(0, rebuild_task.result);
}
TEST_F(TestImageWatcher, NotifySnapCreate) {
REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);

View File

@ -38,10 +38,7 @@ public:
return r;
}
{
RWLock::RLocker l(ictx->owner_lock);
r = librbd::snap_create(ictx, snap_name, true);
}
r = librbd::snap_create(ictx, snap_name);
if (r < 0) {
return r;
}
@ -116,10 +113,7 @@ TEST_F(TestInternal, SnapCreateLocksImage) {
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
{
RWLock::RLocker l(ictx->owner_lock);
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1", true));
}
ASSERT_EQ(0, librbd::snap_create(ictx, "snap1"));
BOOST_SCOPE_EXIT( (ictx) ) {
ASSERT_EQ(0, librbd::snap_remove(ictx, "snap1"));
} BOOST_SCOPE_EXIT_END;
@ -136,8 +130,7 @@ TEST_F(TestInternal, SnapCreateFailsToLockImage) {
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, "manually locked"));
RWLock::RLocker l(ictx->owner_lock);
ASSERT_EQ(-EROFS, librbd::snap_create(ictx, "snap1", true));
ASSERT_EQ(-EROFS, librbd::snap_create(ictx, "snap1"));
}
TEST_F(TestInternal, SnapRollbackLocksImage) {

View File

@ -2719,3 +2719,65 @@ TEST_F(TestLibRBD, UpdateFeatures)
// cannot disable exclusive lock w/ object map
ASSERT_EQ(-EINVAL, image.update_features(RBD_FEATURE_EXCLUSIVE_LOCK, false));
}
TEST_F(TestLibRBD, RebuildObjectMap)
{
librados::IoCtx ioctx;
ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
librbd::RBD rbd;
std::string name = get_temp_image_name();
uint64_t size = 1 << 20;
int order = 18;
ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order));
PrintProgress prog_ctx;
std::string object_map_oid;
bufferlist bl;
bl.append("foo");
{
librbd::Image image;
ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL));
uint64_t features;
ASSERT_EQ(0, image.features(&features));
if ((features & RBD_FEATURE_OBJECT_MAP) == 0) {
ASSERT_EQ(-EINVAL, image.rebuild_object_map(prog_ctx));
return;
}
ASSERT_EQ(bl.length(), image.write(0, bl.length(), bl));
librbd::image_info_t info;
ASSERT_EQ(0, image.stat(info, sizeof(info)));
char prefix[RBD_MAX_BLOCK_NAME_SIZE + 1];
strncpy(prefix, info.block_name_prefix, RBD_MAX_BLOCK_NAME_SIZE);
prefix[RBD_MAX_BLOCK_NAME_SIZE] = '\0';
std::string image_id(prefix + strlen(RBD_DATA_PREFIX));
object_map_oid = RBD_OBJECT_MAP_PREFIX + image_id;
}
// corrupt the object map
ASSERT_EQ(0, ioctx.write(object_map_oid, bl, bl.length(), 0));
librbd::Image image1;
ASSERT_EQ(0, rbd.open(ioctx, image1, name.c_str(), NULL));
uint64_t flags;
ASSERT_EQ(0, image1.get_flags(&flags));
ASSERT_TRUE((flags & RBD_FLAG_OBJECT_MAP_INVALID) != 0);
ASSERT_EQ(0, image1.rebuild_object_map(prog_ctx));
librbd::Image image2;
ASSERT_EQ(0, rbd.open(ioctx, image2, name.c_str(), NULL));
bufferlist read_bl;
ASSERT_EQ(bl.length(), image2.read(0, bl.length(), read_bl));
ASSERT_TRUE(bl.contents_equal(read_bl));
ASSERT_PASSED(validate_object_map, image1);
ASSERT_PASSED(validate_object_map, image2);
}