boost::lambda::_1, &m_image_ctx, m_object_size, m_snapc,
boost::lambda::_2));
AsyncObjectThrottle *throttle = new AsyncObjectThrottle(
- context_factory, create_callback_context(), m_prog_ctx, 0,
+ *this, context_factory, create_callback_context(), m_prog_ctx, 0,
m_overlap_objects);
throttle->start_ops(cct->_conf->rbd_concurrent_management_ops);
}
// vim: ts=8 sw=2 smarttab
#include "librbd/AsyncObjectThrottle.h"
#include "include/rbd/librbd.hpp"
+#include "librbd/AsyncRequest.h"
namespace librbd
{
-AsyncObjectThrottle::AsyncObjectThrottle(const ContextFactory& context_factory,
+AsyncObjectThrottle::AsyncObjectThrottle(const AsyncRequest& async_request,
+ const ContextFactory& context_factory,
Context *ctx, ProgressContext &prog_ctx,
uint64_t object_no,
uint64_t end_object_no)
: m_lock("librbd::AsyncThrottle::m_lock"),
- m_context_factory(context_factory), m_ctx(ctx), m_prog_ctx(prog_ctx),
- m_object_no(object_no), m_end_object_no(end_object_no), m_current_ops(0),
- m_ret(0)
+ m_async_request(async_request), m_context_factory(context_factory),
+ m_ctx(ctx), m_prog_ctx(prog_ctx), m_object_no(object_no),
+ m_end_object_no(end_object_no), m_current_ops(0), m_ret(0)
{
}
void AsyncObjectThrottle::start_next_op() {
bool done = false;
while (!done) {
- if (m_ret != 0 || m_object_no >= m_end_object_no) {
+ if (m_async_request.is_canceled() && m_ret == 0) {
+ // allow in-flight ops to complete, but don't start new ops
+ m_ret = -ERESTART;
+ return;
+ } else if (m_ret != 0 || m_object_no >= m_end_object_no) {
return;
}
namespace librbd
{
+class AsyncRequest;
class ProgressContext;
class AsyncObjectThrottleFinisher {
typedef boost::function<C_AsyncObjectThrottle*(AsyncObjectThrottle&,
uint64_t)> ContextFactory;
- AsyncObjectThrottle(const ContextFactory& context_factory, Context *ctx,
+ AsyncObjectThrottle(const AsyncRequest &async_request,
+ const ContextFactory& context_factory, Context *ctx,
ProgressContext &prog_ctx, uint64_t object_no,
uint64_t end_object_no);
private:
Mutex m_lock;
+ const AsyncRequest &m_async_request;
ContextFactory m_context_factory;
Context *m_ctx;
ProgressContext &m_prog_ctx;
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
#include "librbd/AsyncRequest.h"
+#include "librbd/ImageCtx.h"
#include "librbd/internal.h"
#include <boost/bind.hpp>
namespace librbd
{
+AsyncRequest::AsyncRequest(ImageCtx &image_ctx, Context *on_finish)
+ : m_image_ctx(image_ctx), m_on_finish(on_finish), m_canceled(false),
+ m_xlist_item(this) {
+ Mutex::Locker l(m_image_ctx.async_ops_lock);
+ m_image_ctx.async_requests.push_back(&m_xlist_item);
+}
+
+AsyncRequest::~AsyncRequest() {
+ Mutex::Locker l(m_image_ctx.async_ops_lock);
+ assert(m_xlist_item.remove_myself());
+ m_image_ctx.async_requests_cond.Signal();
+}
+
librados::AioCompletion *AsyncRequest::create_callback_completion() {
return librados::Rados::aio_create_completion(create_callback_context(),
NULL, rados_ctx_cb);
#include "include/int_types.h"
#include "include/Context.h"
#include "include/rados/librados.hpp"
+#include "include/xlist.h"
namespace librbd {
class AsyncRequest
{
public:
- AsyncRequest(ImageCtx &image_ctx, Context *on_finish)
- : m_image_ctx(image_ctx), m_on_finish(on_finish)
- {
- }
-
- virtual ~AsyncRequest() {}
+ AsyncRequest(ImageCtx &image_ctx, Context *on_finish);
+ virtual ~AsyncRequest();
void complete(int r) {
- if (should_complete(r)) {
+ if (m_canceled) {
+ m_on_finish->complete(-ERESTART);
+ delete this;
+ } else if (should_complete(r)) {
m_on_finish->complete(r);
delete this;
}
virtual void send() = 0;
+ inline bool is_canceled() const {
+ return m_canceled;
+ }
+ inline void cancel() {
+ m_canceled = true;
+ }
+
protected:
ImageCtx &m_image_ctx;
Context *m_on_finish;
Context *create_callback_context();
virtual bool should_complete(int r) = 0;
+
+private:
+ bool m_canceled;
+ xlist<AsyncRequest *>::item m_xlist_item;
};
class C_AsyncRequest : public Context
boost::lambda::bind(boost::lambda::new_ptr<AsyncTrimObjectContext>(),
boost::lambda::_1, &m_image_ctx, boost::lambda::_2));
AsyncObjectThrottle *throttle = new AsyncObjectThrottle(
- context_factory, ctx, m_prog_ctx, m_delete_start, m_num_objects);
+ *this, context_factory, ctx, m_prog_ctx, m_delete_start, m_num_objects);
throttle->start_ops(cct->_conf->rbd_concurrent_management_ops);
}
#include "common/perf_counters.h"
#include "librbd/AsyncOperation.h"
+#include "librbd/AsyncRequest.h"
#include "librbd/internal.h"
#include "librbd/ImageCtx.h"
#include "librbd/ImageWatcher.h"
<< "count=" << async_ops.size() << dendl;
async_ops.front()->add_flush_context(on_finish);
}
+
+ void ImageCtx::cancel_async_requests() {
+ Mutex::Locker l(async_ops_lock);
+ ldout(cct, 10) << "canceling async requests: count="
+ << async_requests.size() << dendl;
+
+ for (xlist<AsyncRequest*>::iterator it = async_requests.begin();
+ !it.end(); ++it) {
+ ldout(cct, 10) << "canceling async request: " << *it << dendl;
+ (*it)->cancel();
+ }
+
+ while (!async_requests.empty()) {
+ async_requests_cond.Wait(async_ops_lock);
+ }
+ }
}
namespace librbd {
class AsyncOperation;
+ class AsyncRequest;
class CopyupRequest;
class ImageWatcher;
class ObjectMap;
std::map<uint64_t, CopyupRequest*> copyup_list;
xlist<AsyncOperation*> async_ops;
+ xlist<AsyncRequest*> async_requests;
+ Cond async_requests_cond;
ObjectMap *object_map;
void flush_async_operations();
void flush_async_operations(Context *on_finish);
+
+ void cancel_async_requests();
};
}
bool ImageWatcher::is_lock_owner() const {
assert(m_image_ctx.owner_lock.is_locked());
- return (m_lock_owner_state == LOCK_OWNER_STATE_LOCKED);
+ return (m_lock_owner_state == LOCK_OWNER_STATE_LOCKED ||
+ m_lock_owner_state == LOCK_OWNER_STATE_RELEASING);
}
int ImageWatcher::register_watch() {
return 0;
}
+void ImageWatcher::prepare_unlock() {
+ assert(m_image_ctx.owner_lock.is_wlocked());
+ if (m_lock_owner_state == LOCK_OWNER_STATE_LOCKED) {
+ m_lock_owner_state = LOCK_OWNER_STATE_RELEASING;
+ }
+}
+
+void ImageWatcher::cancel_unlock() {
+ assert(m_image_ctx.owner_lock.is_wlocked());
+ if (m_lock_owner_state == LOCK_OWNER_STATE_RELEASING) {
+ m_lock_owner_state = LOCK_OWNER_STATE_LOCKED;
+ }
+}
+
int ImageWatcher::unlock()
{
assert(m_image_ctx.owner_lock.is_wlocked());
void ImageWatcher::release_lock()
{
+ ldout(m_image_ctx.cct, 10) << "releasing exclusive lock by request" << dendl;
+ {
+ RWLock::WLocker l(m_image_ctx.owner_lock);
+ prepare_unlock();
+ }
+
+ m_image_ctx.cancel_async_requests();
+
RWLock::WLocker l(m_image_ctx.owner_lock);
{
RWLock::WLocker l2(m_image_ctx.md_lock);
bufferlist *out) {
RWLock::RLocker l(m_image_ctx.owner_lock);
- if (is_lock_owner()) {
+ 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()) {
void ImageWatcher::handle_payload(const ResizePayload &payload,
bufferlist *out) {
RWLock::RLocker l(m_image_ctx.owner_lock);
- if (is_lock_owner()) {
+ 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()) {
void ImageWatcher::handle_payload(const SnapCreatePayload &payload,
bufferlist *out) {
RWLock::RLocker l(m_image_ctx.owner_lock);
- if (is_lock_owner()) {
+ 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 try_lock();
int request_lock(const boost::function<int(AioCompletion*)>& restart_op,
AioCompletion* c);
+ void prepare_unlock();
+ void cancel_unlock();
int unlock();
void assert_header_locked(librados::ObjectWriteOperation *op);
enum LockOwnerState {
LOCK_OWNER_STATE_NOT_LOCKED,
- LOCK_OWNER_STATE_LOCKED
+ LOCK_OWNER_STATE_LOCKED,
+ LOCK_OWNER_STATE_RELEASING
};
enum WatchState {
// ignore return value, since we may be set to a non-existent
// snapshot and the user is trying to fix that
ictx_check(ictx);
+
+ bool unlocking = false;
+ {
+ RWLock::WLocker l(ictx->owner_lock);
+ if (ictx->image_watcher != NULL && ictx->image_watcher->is_lock_owner() &&
+ snap_name != NULL && strlen(snap_name) != 0) {
+ // stop incoming requests since we will release the lock
+ ictx->image_watcher->prepare_unlock();
+ unlocking = true;
+ }
+ }
+
+ ictx->cancel_async_requests();
ictx->flush_async_operations();
if (ictx->object_cacher) {
// complete pending writes before we're set to a snapshot and
}
int r = _snap_set(ictx, snap_name);
if (r < 0) {
+ RWLock::WLocker l(ictx->owner_lock);
+ if (unlocking) {
+ ictx->image_watcher->cancel_unlock();
+ }
return r;
}
RWLock::WLocker l(ictx->owner_lock);
if (ictx->image_watcher != NULL) {
- if (!ictx->image_watcher->is_lock_supported() &&
- ictx->image_watcher->is_lock_owner()) {
+ if (unlocking) {
r = ictx->image_watcher->unlock();
if (r < 0) {
lderr(ictx->cct) << "error unlocking image: " << cpp_strerror(r)
{
ldout(ictx->cct, 20) << "close_image " << ictx << dendl;
+ {
+ RWLock::WLocker l(ictx->owner_lock);
+ if (ictx->image_watcher != NULL && ictx->image_watcher->is_lock_owner()) {
+ // stop incoming requests
+ ictx->image_watcher->prepare_unlock();
+ }
+ }
+
+ ictx->cancel_async_requests();
ictx->readahead.wait_for_pending();
if (ictx->object_cacher) {
ictx->shutdown_cache(); // implicitly flushes
ASSERT_FALSE(is_owner);
ASSERT_FALSE(c->is_complete());
}
+
+TEST_F(TestInternal, CancelAsyncResize) {
+ REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ {
+ RWLock::WLocker l(ictx->owner_lock);
+ ASSERT_EQ(0, ictx->image_watcher->try_lock());
+ ASSERT_TRUE(ictx->image_watcher->is_lock_owner());
+ }
+
+ uint64_t size;
+ ASSERT_EQ(0, librbd::get_size(ictx, &size));
+
+ uint32_t attempts = 0;
+ while (attempts++ < 20 && size > 0) {
+ C_SaferCond ctx;
+ librbd::NoOpProgressContext prog_ctx;
+
+ size -= MIN(size, 1<<18);
+ {
+ RWLock::RLocker l(ictx->owner_lock);
+ ASSERT_EQ(0, librbd::async_resize(ictx, &ctx, size, prog_ctx));
+ }
+
+ // try to interrupt the in-progress resize
+ ictx->cancel_async_requests();
+
+ int r = ctx.wait();
+ if (r == -ERESTART) {
+ std::cout << "detected canceled async request" << std::endl;
+ break;
+ }
+ ASSERT_EQ(0, r);
+ }
+}