librbd: v3 copy-on-read for clones
cover librbd: v3 copy-on-read for clones
Rbd clone shares objects with its parent, unless writing data to clone. When read data from clone, it copy data from its parent if
object doesn't exits in clone. Each time read the same object(not in clone), the clone must send request to its parent. Usually the network latency is bottleneck of read performance. If store the object data into clone after copyup it from parent, then the next time read the same object just in clone, which can improve read performance.
Copy entire object from parent,no matter read entire or part of object.
Min Chen (3):
librbd: copy-on-read for clones, add an option for copy-on-read
librbd: copy-on-read for clones, read entire object from parent
librbd: copy-on-read for clones, write entire object into child
asychronously
src/common/config_opts.h | 1 + src/include/xlist.h | 1 + src/librbd/AioRequest.cc | 88 ++++++++++++++++++++++++++++++++++++++++++++-- src/librbd/AioRequest.h | 4 +++ src/librbd/ImageCtx.cc | 68 +++++++++++++++++++++++++++++++++++ src/librbd/ImageCtx.h | 6 ++++ src/librbd/internal.cc | 4 +++ 7 files changed, 169 insertions(+), 3 deletions(-)
Signature · 1 line
1.7.10.4
PATCH 1/3 librbd: v3 copy-on-read for clones, add an option for copy-on-read
Add an option rbd_clone_copy_on_read to enable copy-on-read
--- src/common/config_opts.h | 1 + src/librbd/AioRequest.cc | 4 ++++ 2 files changed, 5 insertions(+)
src/common/config_opts.h +1 -0
@@ -711,6 +711,7 @@ OPTION(rbd_balance_snap_reads, OPT_BOOL, false) OPTION(rbd_localize_snap_reads, OPT_BOOL, false) OPTION(rbd_balance_parent_reads, OPT_BOOL, false) OPTION(rbd_localize_parent_reads, OPT_BOOL, true) +OPTION (rbd_clone_copy_on_read,OPT_BOOL , false)//copy-on-read option for rbd clones /* * The following options change the behavior for librbd's image creation methods that
src/librbd/AioRequest.cc +4 -0
@@ -59,6 +59,10 @@ namespace librbd { ldout(m_ictx->cct, 20) << "should_complete " << this << " " << m_oid << " " << m_object_off << "~" << m_object_len << " r = " << r << dendl; + //get copy-on-read option and check image if read_only + bool cor = (m_ictx->cct->_conf->rbd_clone_copy_on_read) && (!m_ictx->read_only); + ldout(m_ictx->cct, 20) << "should_complete cor = " << cor << " read_only = " << m_ictx->read_only << dendl; + if (!m_tried_parent && r == -ENOENT) { RWLock::RLocker l(m_ictx->snap_lock); RWLock::RLocker l2(m_ictx->parent_lock);
Signature · 1 line
1.7.10.4
PATCH 2/3 librbd: v3 copy-on-read for clones, read entire object from parent
In reply to Min Chen Rbd copy-on-read is implmented in AioRequest. If object extent doesn't exist in clone, then read
the entire range of object from parent. Bufferlist m_entire_data is added to AioRequest to save object instead of m_read_data which just keeps data read
--- src/librbd/AioRequest.cc | 51 +++++++++++++++++++++++++++++++++++++++++++--- src/librbd/AioRequest.h | 3 +++ 2 files changed, 51 insertions(+), 3 deletions(-)
src/librbd/AioRequest.cc +48 -3
@@ -22,7 +22,7 @@ namespace librbd { m_ictx(NULL), m_ioctx(NULL), m_object_no(0), m_object_off(0), m_object_len(0), m_snap_id(CEPH_NOSNAP), m_completion(NULL), m_parent_completion(NULL), - m_hide_enoent(false) {} + m_hide_enoent(false), m_parent_completion_cor(NULL){} AioRequest::AioRequest(ImageCtx *ictx, const std::string &oid, uint64_t objectno, uint64_t off, uint64_t len, librados::snap_t snap_id,
@@ -31,13 +31,17 @@ namespace librbd { m_ictx(ictx), m_ioctx(&ictx->data_ctx), m_oid(oid), m_object_no(objectno), m_object_off(off), m_object_len(len), m_snap_id(snap_id), m_completion(completion), m_parent_completion(NULL), - m_hide_enoent(hide_enoent) {} + m_hide_enoent(hide_enoent), m_parent_completion_cor(NULL) {} AioRequest::~AioRequest() { if (m_parent_completion) { m_parent_completion->release(); m_parent_completion = NULL; } + if (m_parent_completion_cor) { + m_parent_completion_cor->release(); + m_parent_completion_cor = NULL; + } } void AioRequest::read_from_parent(vector<pair<uint64_t,uint64_t> >& image_extents)
@@ -52,6 +56,19 @@ namespace librbd { m_parent_completion); } + //copy-on-read : read the entire object from parent, using bufferlist m_entire_object + void AioRequest::read_from_parent_cor(vector<pair<uint64_t,uint64_t> >& image_extents) + { + assert(!m_parent_completion_cor); + m_parent_completion_cor = aio_create_completion_internal(this, rbd_req_cb); + ldout(m_ictx->cct, 20) << "read_from_parent_cor this = " << this + << " parent completion cor " << m_parent_completion_cor + << " extents " << image_extents + << dendl; + aio_read(m_ictx->parent, image_extents, NULL, &m_entire_object, + m_parent_completion_cor); + } + /** read **/ bool AioRead::should_complete(int r)
@@ -81,11 +98,39 @@ namespace librbd { uint64_t object_overlap = m_ictx->prune_parent_extents(image_extents, image_overlap); if (object_overlap) { m_tried_parent = true; - read_from_parent(image_extents); + if (cor) {//copy-on-read option + vector<pair<uint64_t,uint64_t> > extend_image_extents; + //extend range to entire object + Striper::extent_to_file(m_ictx->cct, &m_ictx->layout, + m_object_no, 0, m_ictx->layout.fl_object_size, + extend_image_extents); + //read entire object from parent , and put it in m_entire_object + read_from_parent_cor(extend_image_extents); + } else { + read_from_parent(image_extents); + } return false; } } + if (cor) {//copy-on-read option + //if read entire object from parent success + if (m_tried_parent && r > 0) { + vector<pair<uint64_t,uint64_t> > image_extents; + Striper::extent_to_file(m_ictx->cct, &m_ictx->layout, + m_object_no, m_object_off, m_object_len, + image_extents); + uint64_t image_overlap = 0; + int r = m_ictx->get_parent_overlap(m_snap_id, &image_overlap); + if (r < 0) { + assert(0 == "FIXME"); + } + m_ictx->prune_parent_extents(image_extents, image_overlap); + // copy the read range to m_read_data + m_read_data.substr_of(m_entire_object, m_object_off, m_object_len); + } + } + return true; }
src/librbd/AioRequest.h +3 -0
@@ -47,6 +47,7 @@ namespace librbd { protected: void read_from_parent(vector<pair<uint64_t,uint64_t> >& image_extents); + void read_from_parent_cor(vector<pair<uint64_t,uint64_t> >& image_extents);ImageCtx *m_ictx; librados::IoCtx *m_ioctx;
@@ -57,6 +58,8 @@ namespace librbd {AioCompletion *m_parent_completion; ceph::bufferlist m_read_data;bool m_hide_enoent; + ceph::bufferlist m_entire_object;//copy-on-read : store the entire object +AioCompletion *m_parent_completion_cor;//copy-on-read : AioCompletion for read from parent }; class AioRead : public AioRequest {
Signature · 1 line
1.7.10.4
PATCH 3/3 librbd: v3 copy-on-read for clones, write entire object into child asychronously
In reply to Min Chen The object has been already saved in m_entire_object bufferlist.
Send "copyup" request with m_entire_object, then cls_rbd method copyup will write the object data to child. AioCompletion is used to protect copyup aio operation, and remove it after copyup request finished. Add a function xlist::iterator:: item *get_cur() {return cur;} in include/xlist.h
to support removing item in xlist.
--- src/include/xlist.h | 1 + src/librbd/AioRequest.cc | 33 ++++++++++++++++++++++ src/librbd/AioRequest.h | 1 + src/librbd/ImageCtx.cc | 68 ++++++++++++++++++++++++++++++++++++++++++++++ src/librbd/ImageCtx.h | 6 ++++ src/librbd/internal.cc | 4 +++ 6 files changed, 113 insertions(+)
src/include/xlist.h +1 -0
@@ -157,6 +157,7 @@ public:return *this; } bool end() const { return cur == 0; } +item *get_cur () const {return cur; } };iterator begin () {return iterator (_front); }
src/librbd/AioRequest.cc +33 -0
@@ -71,6 +71,38 @@ namespace librbd { /** read **/ + //copy-on-read: after read entire object, just write it into child + ssize_t AioRead::write_cor() + { + ldout(m_ictx->cct, 20) << "write_cor" << dendl; + int ret = 0; + + m_ictx->snap_lock.get_read(); + ::SnapContext snapc = m_ictx->snapc; + m_ictx->snap_lock.put_read(); + + librados::ObjectWriteOperation copyup_cor; + copyup_cor.exec("rbd", "copyup", m_entire_object); + + std::vector<librados::snap_t> m_snaps; + for (std::vector<snapid_t>::const_iterator it = snapc.snaps.begin(); + it != snapc.snaps.end(); ++it) { + m_snaps.push_back(it->val); + } + + librados::AioCompletion *cor_completion = + librados::Rados::aio_create_completion(m_ictx, librbd::cor_completion_callback, NULL); + + xlist<librados::AioCompletion *>::item *comp = + new xlist<librados::AioCompletion *>::item(cor_completion); + + m_ictx->add_cor_completion(comp);//add cor_completion to xlist + //asynchronously write object + ret = m_ictx->md_ctx.aio_operate(m_oid, cor_completion, ©up_cor, snapc.seq.val, m_snaps); + + return ret; + } + bool AioRead::should_complete(int r) { ldout(m_ictx->cct, 20) << "should_complete " << this << " " << m_oid << " " << m_object_off << "~" << m_object_len
@@ -128,6 +160,7 @@ namespace librbd { m_ictx->prune_parent_extents(image_extents, image_overlap); // copy the read range to m_read_data m_read_data.substr_of(m_entire_object, m_object_off, m_object_len); + write_cor(); } }
src/librbd/AioRequest.h +1 -0
@@ -75,6 +75,7 @@ namespace librbd { m_tried_parent(false), m_sparse(sparse) { } virtual ~AioRead() {} +ssize_t write_cor (); virtual bool should_complete(int r); virtual int send();
src/librbd/ImageCtx.cc +68 -0
90 lines not on this page · raw message
src/librbd/ImageCtx.h +6 -0
@@ -68,6 +68,7 @@ namespace librbd {RWLock snap_lock;// protects snapshot-related member variables: RWLock parent_lock;// protects parent_md and parent Mutex refresh_lock;// protects refresh_seq and last_refresh +Mutex cor_lock;//protects cor_completions for copy-on-read unsigned extra_read_flags;
@@ -89,6 +90,8 @@ namespace librbd {LibrbdWriteback *writeback_handler; ObjectCacher::ObjectSet *object_set; + xlist<librados::AioCompletion*> *cor_completions; //copy-on-read AioCompletions +/** * Either image_name or image_id must be set. * If id is not known, pass the empty std::string,
@@ -148,7 +151,10 @@ namespace librbd { uint64_t prune_parent_extents(vector<pair<uint64_t,uint64_t> >& objectx, uint64_t overlap); + void add_cor_completion(xlist<librados::AioCompletion*>::item *comp); + void wait_last_completions();//wait for uncompleted asynchronous write which is still in xlist }; + void cor_completion_callback(librados::completion_t aio_completion_impl, void *arg); }#endif
src/librbd/internal.cc +4 -0
@@ -2101,6 +2101,10 @@ reprotect_and_return_err: void close_image(ImageCtx *ictx) { ldout(ictx->cct, 20) << "close_image " << ictx << dendl; + + if (ictx->cor_completions) + ictx->wait_last_completions();//copy-on-read: wait for unfinished AioCompletion requests + if (ictx->object_cacher) ictx->shutdown_cache(); // implicitly flushes else
Signature · 1 line
1.7.10.4