mirror of
https://github.com/ceph/ceph
synced 2025-03-30 23:40:09 +00:00
Merge pull request #4140 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:
commit
458901dfb2
doc/man/8
src
cls/rbd
include/rbd
librbd
AioRequest.ccAsyncTrimRequest.ccCopyupRequest.ccImageWatcher.ccImageWatcher.hMakefile.amObjectMap.ccObjectMap.hRebuildObjectMapRequest.ccRebuildObjectMapRequest.hWatchNotifyTypes.ccWatchNotifyTypes.hinternal.ccinternal.hlibrbd.cc
rbd.cctest
cli/rbd
cls_rbd
librados_test_stub
librbd
@ -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.
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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> ¤t_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
|
||||
|
352
src/librbd/RebuildObjectMapRequest.cc
Normal file
352
src/librbd/RebuildObjectMapRequest.cc
Normal 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
|
78
src/librbd/RebuildObjectMapRequest.h
Normal file
78
src/librbd/RebuildObjectMapRequest.h
Normal 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
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
80
src/rbd.cc
80
src/rbd.cc
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user