diff options
author | Julien Collet <julien.collet@cern.ch> | 2018-02-20 10:14:00 +0100 |
---|---|---|
committer | Jason Dillaman <dillaman@redhat.com> | 2018-08-21 22:17:17 +0200 |
commit | 809c5430c2929ea1b33d3d1ab7c0023fbb2ce7a5 (patch) | |
tree | 7d11af46ef75621e0674697c479e31f4b7f15888 /src | |
parent | Merge pull request #23649 from trociny/wip-26939 (diff) | |
download | ceph-809c5430c2929ea1b33d3d1ab7c0023fbb2ce7a5.tar.xz ceph-809c5430c2929ea1b33d3d1ab7c0023fbb2ce7a5.zip |
librbd: add image access/last modified timestamps
Add access and modify timestamps and associated tests
to RBD images.
Access (resp. modify) timestamps are updated on
read (resp. write) operations. A configurable throttling
mechanism is implemented (default to 60s).
Signed-off-by: Julien Collet <julien.collet@cern.ch>
Diffstat (limited to 'src')
-rw-r--r-- | src/cls/rbd/cls_rbd.cc | 156 | ||||
-rw-r--r-- | src/cls/rbd/cls_rbd_client.cc | 92 | ||||
-rw-r--r-- | src/cls/rbd/cls_rbd_client.h | 19 | ||||
-rw-r--r-- | src/common/options.cc | 10 | ||||
-rw-r--r-- | src/include/rbd/librbd.h | 4 | ||||
-rw-r--r-- | src/include/rbd/librbd.hpp | 2 | ||||
-rw-r--r-- | src/librbd/ImageCtx.cc | 27 | ||||
-rw-r--r-- | src/librbd/ImageCtx.h | 12 | ||||
-rw-r--r-- | src/librbd/image/OpenRequest.cc | 44 | ||||
-rw-r--r-- | src/librbd/image/OpenRequest.h | 6 | ||||
-rw-r--r-- | src/librbd/io/ImageRequest.cc | 86 | ||||
-rw-r--r-- | src/librbd/librbd.cc | 64 | ||||
-rw-r--r-- | src/pybind/rbd/rbd.pyx | 26 | ||||
-rw-r--r-- | src/test/cli-integration/rbd/formatted-output.t | 48 | ||||
-rw-r--r-- | src/test/cls_rbd/test_cls_rbd.cc | 28 | ||||
-rw-r--r-- | src/test/librbd/io/test_mock_ImageRequest.cc | 114 | ||||
-rw-r--r-- | src/test/librbd/mock/MockImageCtx.h | 16 | ||||
-rw-r--r-- | src/test/librbd/test_librbd.cc | 54 | ||||
-rw-r--r-- | src/test/pybind/test_rbd.py | 10 | ||||
-rw-r--r-- | src/tools/rbd/action/Info.cc | 49 | ||||
-rw-r--r-- | src/tracing/librbd.tp | 45 |
21 files changed, 894 insertions, 18 deletions
diff --git a/src/cls/rbd/cls_rbd.cc b/src/cls/rbd/cls_rbd.cc index b3579579694..ed2163dbf4c 100644 --- a/src/cls/rbd/cls_rbd.cc +++ b/src/cls/rbd/cls_rbd.cc @@ -520,15 +520,15 @@ int create(cls_method_context_t hctx, bufferlist *in, bufferlist *out) bufferlist featuresbl; bufferlist object_prefixbl; bufferlist snap_seqbl; - bufferlist create_timestampbl; + bufferlist timestampbl; uint64_t snap_seq = 0; - utime_t create_timestamp = ceph_clock_now(); + utime_t timestamp = ceph_clock_now(); encode(size, sizebl); encode(order, orderbl); encode(features, featuresbl); encode(object_prefix, object_prefixbl); encode(snap_seq, snap_seqbl); - encode(create_timestamp, create_timestampbl); + encode(timestamp, timestampbl); map<string, bufferlist> omap_vals; omap_vals["size"] = sizebl; @@ -536,7 +536,9 @@ int create(cls_method_context_t hctx, bufferlist *in, bufferlist *out) omap_vals["features"] = featuresbl; omap_vals["object_prefix"] = object_prefixbl; omap_vals["snap_seq"] = snap_seqbl; - omap_vals["create_timestamp"] = create_timestampbl; + omap_vals["create_timestamp"] = timestampbl; + omap_vals["access_timestamp"] = timestampbl; + omap_vals["modify_timestamp"] = timestampbl; if ((features & RBD_FEATURE_OPERATIONS) != 0ULL) { CLS_ERR("Attempting to set internal feature: operations"); @@ -1094,6 +1096,81 @@ int get_create_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist * } /** + * get the image access timestamp + * + * Input: + * @param none + * + * Output: + * @param timestamp the image access timestamp + * + * @returns 0 on success, negative error code upon failure + */ +int get_access_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "get_access_timestamp"); + + utime_t timestamp; + bufferlist bl; + int r = cls_cxx_map_get_val(hctx, "access_timestamp", &bl); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading access_timestamp: %s", cpp_strerror(r).c_str()); + return r; + } + } else { + try { + auto it = bl.cbegin(); + decode(timestamp, it); + } catch (const buffer::error &err) { + CLS_ERR("could not decode access_timestamp"); + return -EIO; + } + } + + encode(timestamp, *out); + return 0; +} + +/** + * get the image modify timestamp + * + * Input: + * @param none + * + * Output: + * @param timestamp the image modify timestamp + * + * @returns 0 on success, negative error code upon failure + */ +int get_modify_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + CLS_LOG(20, "get_modify_timestamp"); + + utime_t timestamp; + bufferlist bl; + int r = cls_cxx_map_get_val(hctx, "modify_timestamp", &bl); + if (r < 0) { + if (r != -ENOENT) { + CLS_ERR("error reading modify_timestamp: %s", cpp_strerror(r).c_str()); + return r; + } + } else { + try { + auto it = bl.cbegin(); + decode(timestamp, it); + } catch (const buffer::error &err) { + CLS_ERR("could not decode modify_timestamp"); + return -EIO; + } + } + + encode(timestamp, *out); + return 0; +} + + +/** * get the image flags * * Input: @@ -2393,6 +2470,59 @@ int set_id(cls_method_context_t hctx, bufferlist *in, bufferlist *out) return cls_cxx_write(hctx, 0, write_bl.length(), &write_bl); } +/** + * Update the access timestamp of an image + * + * Input: + * @param none + * + * Output: + * @returns 0 on success, negative error code on other error + */ +int set_access_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + int r = check_exists(hctx); + if(r < 0) + return r; + + utime_t timestamp = ceph_clock_now(); + r = write_key(hctx, "access_timestamp", timestamp); + if(r < 0) { + CLS_ERR("error setting access_timestamp"); + return r; + } + + return 0; +} + +/** + * Update the modify timestamp of an image + * + * Input: + * @param none + * + * Output: + * @returns 0 on success, negative error code on other error + */ + +int set_modify_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + int r = check_exists(hctx); + if(r < 0) + return r; + + utime_t timestamp = ceph_clock_now(); + r = write_key(hctx, "modify_timestamp", timestamp); + if(r < 0) { + CLS_ERR("error setting modify_timestamp"); + return r; + } + + return 0; +} + + + /*********************** methods for rbd_directory ***********************/ static const string dir_key_for_id(const string &id) @@ -6651,6 +6781,8 @@ CLS_INIT(rbd) cls_method_handle_t h_get_stripe_unit_count; cls_method_handle_t h_set_stripe_unit_count; cls_method_handle_t h_get_create_timestamp; + cls_method_handle_t h_get_access_timestamp; + cls_method_handle_t h_get_modify_timestamp; cls_method_handle_t h_get_flags; cls_method_handle_t h_set_flags; cls_method_handle_t h_op_features_get; @@ -6673,6 +6805,8 @@ CLS_INIT(rbd) cls_method_handle_t h_copyup; cls_method_handle_t h_get_id; cls_method_handle_t h_set_id; + cls_method_handle_t h_set_modify_timestamp; + cls_method_handle_t h_set_access_timestamp; cls_method_handle_t h_dir_get_id; cls_method_handle_t h_dir_get_name; cls_method_handle_t h_dir_list; @@ -6828,6 +6962,12 @@ CLS_INIT(rbd) cls_register_cxx_method(h_class, "get_create_timestamp", CLS_METHOD_RD, get_create_timestamp, &h_get_create_timestamp); + cls_register_cxx_method(h_class, "get_access_timestamp", + CLS_METHOD_RD, + get_access_timestamp, &h_get_access_timestamp); + cls_register_cxx_method(h_class, "get_modify_timestamp", + CLS_METHOD_RD, + get_modify_timestamp, &h_get_modify_timestamp); cls_register_cxx_method(h_class, "get_flags", CLS_METHOD_RD, get_flags, &h_get_flags); @@ -6883,6 +7023,14 @@ CLS_INIT(rbd) assert_snapc_seq, &h_assert_snapc_seq); + cls_register_cxx_method(h_class, "set_modify_timestamp", + CLS_METHOD_RD | CLS_METHOD_WR, + set_modify_timestamp, &h_set_modify_timestamp); + + cls_register_cxx_method(h_class, "set_access_timestamp", + CLS_METHOD_RD | CLS_METHOD_WR, + set_access_timestamp, &h_set_access_timestamp); + /* methods for the rbd_children object */ cls_register_cxx_method(h_class, "add_child", CLS_METHOD_RD | CLS_METHOD_WR, diff --git a/src/cls/rbd/cls_rbd_client.cc b/src/cls/rbd/cls_rbd_client.cc index 087ed597b23..88317f893bc 100644 --- a/src/cls/rbd/cls_rbd_client.cc +++ b/src/cls/rbd/cls_rbd_client.cc @@ -1065,6 +1065,32 @@ namespace librbd { return ioctx->operate(oid, &op); } + void set_modify_timestamp(librados::ObjectWriteOperation *op) + { + bufferlist empty_bl; + op->exec("rbd","set_modify_timestamp",empty_bl); + } + + int set_modify_timestamp(librados::IoCtx *ioctx, const std::string &oid) + { + librados::ObjectWriteOperation op; + set_modify_timestamp(&op); + return ioctx->operate(oid, &op); + } + + void set_access_timestamp(librados::ObjectWriteOperation *op) + { + bufferlist empty_bl; + op->exec("rbd","set_access_timestamp",empty_bl); + } + + int set_access_timestamp(librados::IoCtx *ioctx, const std::string &oid) + { + librados::ObjectWriteOperation op; + set_access_timestamp(&op); + return ioctx->operate(oid, &op); + } + void get_create_timestamp_start(librados::ObjectReadOperation *op) { bufferlist empty_bl; op->exec("rbd", "get_create_timestamp", empty_bl); @@ -1098,6 +1124,72 @@ namespace librbd { return get_create_timestamp_finish(&it, timestamp); } + void get_access_timestamp_start(librados::ObjectReadOperation *op) { + bufferlist empty_bl; + op->exec("rbd", "get_access_timestamp", empty_bl); + } + + int get_access_timestamp_finish(bufferlist::const_iterator *it, + utime_t *timestamp) { + assert(timestamp); + + try { + decode(*timestamp, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; + } + + int get_access_timestamp(librados::IoCtx *ioctx, const std::string &oid, + utime_t *timestamp) + { + librados::ObjectReadOperation op; + get_access_timestamp_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_access_timestamp_finish(&it, timestamp); + } + + void get_modify_timestamp_start(librados::ObjectReadOperation *op) { + bufferlist empty_bl; + op->exec("rbd", "get_modify_timestamp", empty_bl); + } + + int get_modify_timestamp_finish(bufferlist::const_iterator *it, + utime_t *timestamp) { + assert(timestamp); + + try { + decode(*timestamp, *it); + } catch (const buffer::error &err) { + return -EBADMSG; + } + return 0; + } + + int get_modify_timestamp(librados::IoCtx *ioctx, const std::string &oid, + utime_t *timestamp) + { + librados::ObjectReadOperation op; + get_modify_timestamp_start(&op); + + bufferlist out_bl; + int r = ioctx->operate(oid, &op, &out_bl); + if (r < 0) { + return r; + } + + auto it = out_bl.cbegin(); + return get_modify_timestamp_finish(&it, timestamp); + } + /************************ rbd_id object methods ************************/ void get_id_start(librados::ObjectReadOperation *op) { diff --git a/src/cls/rbd/cls_rbd_client.h b/src/cls/rbd/cls_rbd_client.h index 2acc5be4743..c9c362bb536 100644 --- a/src/cls/rbd/cls_rbd_client.h +++ b/src/cls/rbd/cls_rbd_client.h @@ -222,6 +222,25 @@ namespace librbd { utime_t *timestamp); int get_create_timestamp(librados::IoCtx *ioctx, const std::string &oid, utime_t *timestamp); + + void get_access_timestamp_start(librados::ObjectReadOperation *op); + int get_access_timestamp_finish(bufferlist::const_iterator *it, + utime_t *timestamp); + int get_access_timestamp(librados::IoCtx *ioctx, const std::string &oid, + utime_t *timestamp); + + void get_modify_timestamp_start(librados::ObjectReadOperation *op); + int get_modify_timestamp_finish(bufferlist::const_iterator *it, + utime_t *timestamp); + int get_modify_timestamp(librados::IoCtx *ioctx, const std::string &oid, + utime_t *timestamp); + + void set_modify_timestamp(librados::ObjectWriteOperation *op); + int set_modify_timestamp(librados::IoCtx *ioctx, const std::string &oid); + + void set_access_timestamp(librados::ObjectWriteOperation *op); + int set_access_timestamp(librados::IoCtx *ioctx, const std::string &oid); + int metadata_list(librados::IoCtx *ioctx, const std::string &oid, const std::string &start, uint64_t max_return, map<string, bufferlist> *pairs); diff --git a/src/common/options.cc b/src/common/options.cc index 7d52edd0a47..ef57318c92b 100644 --- a/src/common/options.cc +++ b/src/common/options.cc @@ -6632,6 +6632,16 @@ static std::vector<Option> get_rbd_options() { Option("rbd_discard_on_zeroed_write_same", Option::TYPE_BOOL, Option::LEVEL_ADVANCED) .set_default(true) .set_description("discard data on zeroed write same instead of writing zero"), + + Option("rbd_mtime_update_interval", Option::TYPE_UINT, Option::LEVEL_ADVANCED) + .set_default(60) + .set_min(0) + .set_description("RBD Image modify timestamp refresh interval. Set to 0 to disable modify timestamp update."), + + Option("rbd_atime_update_interval", Option::TYPE_UINT, Option::LEVEL_ADVANCED) + .set_default(60) + .set_min(0) + .set_description("RBD Image access timestamp refresh interval. Set to 0 to disable access timestamp update."), }); } diff --git a/src/include/rbd/librbd.h b/src/include/rbd/librbd.h index 045c0dfe15b..66d5444e943 100644 --- a/src/include/rbd/librbd.h +++ b/src/include/rbd/librbd.h @@ -453,6 +453,10 @@ CEPH_RBD_API int rbd_get_stripe_count(rbd_image_t image, CEPH_RBD_API int rbd_get_create_timestamp(rbd_image_t image, struct timespec *timestamp); +CEPH_RBD_API int rbd_get_access_timestamp(rbd_image_t image, + struct timespec *timestamp); +CEPH_RBD_API int rbd_get_modify_timestamp(rbd_image_t image, + struct timespec *timestamp); CEPH_RBD_API int rbd_get_overlap(rbd_image_t image, uint64_t *overlap); CEPH_RBD_API int rbd_get_name(rbd_image_t image, char *name, size_t *name_len); diff --git a/src/include/rbd/librbd.hpp b/src/include/rbd/librbd.hpp index 62b3081f750..c6270d33109 100644 --- a/src/include/rbd/librbd.hpp +++ b/src/include/rbd/librbd.hpp @@ -378,6 +378,8 @@ public: uint64_t get_stripe_count() const; int get_create_timestamp(struct timespec *timestamp); + int get_access_timestamp(struct timespec *timestamp); + int get_modify_timestamp(struct timespec *timestamp); int flatten(); int flatten_with_progress(ProgressContext &prog_ctx); diff --git a/src/librbd/ImageCtx.cc b/src/librbd/ImageCtx.cc index 3abb4f677eb..1552829d2e0 100644 --- a/src/librbd/ImageCtx.cc +++ b/src/librbd/ImageCtx.cc @@ -107,6 +107,7 @@ public: owner_lock(util::unique_lock_name("librbd::ImageCtx::owner_lock", this)), md_lock(util::unique_lock_name("librbd::ImageCtx::md_lock", this)), snap_lock(util::unique_lock_name("librbd::ImageCtx::snap_lock", this)), + timestamp_lock(util::unique_lock_name("librbd::ImageCtx::timestamp_lock", this)), parent_lock(util::unique_lock_name("librbd::ImageCtx::parent_lock", this)), object_map_lock(util::unique_lock_name("librbd::ImageCtx::object_map_lock", this)), async_ops_lock(util::unique_lock_name("librbd::ImageCtx::async_ops_lock", this)), @@ -435,6 +436,28 @@ public: return create_timestamp; } + utime_t ImageCtx::get_access_timestamp() const + { + return access_timestamp; + } + + utime_t ImageCtx::get_modify_timestamp() const + { + return modify_timestamp; + } + + void ImageCtx::set_access_timestamp(utime_t at) + { + assert(timestamp_lock.is_wlocked()); + access_timestamp = at; + } + + void ImageCtx::set_modify_timestamp(utime_t mt) + { + assert(timestamp_lock.is_locked()); + modify_timestamp = mt; + } + int ImageCtx::is_snap_protected(snap_t in_snap_id, bool *is_protected) const { @@ -776,6 +799,8 @@ public: "rbd_mirroring_resync_after_disconnect", false)( "rbd_mirroring_delete_delay", false)( "rbd_mirroring_replay_delay", false)( + "rbd_mtime_update_interval", false)( + "rbd_atime_update_interval", false)( "rbd_skip_partial_discard", false)( "rbd_qos_iops_limit", false)( "rbd_qos_bps_limit", false)( @@ -842,6 +867,8 @@ public: ASSIGN_OPTION(mirroring_resync_after_disconnect, bool); ASSIGN_OPTION(mirroring_delete_delay, uint64_t); ASSIGN_OPTION(mirroring_replay_delay, int64_t); + ASSIGN_OPTION(mtime_update_interval, uint64_t); + ASSIGN_OPTION(atime_update_interval, uint64_t); ASSIGN_OPTION(skip_partial_discard, bool); ASSIGN_OPTION(blkin_trace_all, bool); ASSIGN_OPTION(qos_iops_limit, uint64_t); diff --git a/src/librbd/ImageCtx.h b/src/librbd/ImageCtx.h index 259227870e3..f623baffb65 100644 --- a/src/librbd/ImageCtx.h +++ b/src/librbd/ImageCtx.h @@ -91,7 +91,7 @@ namespace librbd { * Lock ordering: * * owner_lock, md_lock, snap_lock, parent_lock, - * object_map_lock, async_op_lock + * object_map_lock, async_op_lock, timestamp_lock */ RWLock owner_lock; // protects exclusive lock leadership updates RWLock md_lock; // protects access to the mutable image metadata that @@ -104,6 +104,7 @@ namespace librbd { // lockers RWLock snap_lock; // protects snapshot-related member variables, // features (and associated helper classes), and flags + RWLock timestamp_lock; RWLock parent_lock; // protects parent_md and parent RWLock object_map_lock; // protects object map updates and object_map itself Mutex async_ops_lock; // protects async_ops and async_requests @@ -131,6 +132,8 @@ namespace librbd { uint64_t op_features = 0; bool operations_disabled = false; utime_t create_timestamp; + utime_t access_timestamp; + utime_t modify_timestamp; file_layout_t layout; @@ -202,6 +205,8 @@ namespace librbd { int mirroring_replay_delay; bool skip_partial_discard; bool blkin_trace_all; + uint64_t mtime_update_interval; + uint64_t atime_update_interval; uint64_t qos_iops_limit; uint64_t qos_bps_limit; uint64_t qos_read_iops_limit; @@ -275,6 +280,11 @@ namespace librbd { uint64_t get_stripe_count() const; uint64_t get_stripe_period() const; utime_t get_create_timestamp() const; + utime_t get_access_timestamp() const; + utime_t get_modify_timestamp() const; + + void set_access_timestamp(utime_t at); + void set_modify_timestamp(utime_t at); void add_snap(cls::rbd::SnapshotNamespace in_snap_namespace, std::string in_snap_name, diff --git a/src/librbd/image/OpenRequest.cc b/src/librbd/image/OpenRequest.cc index 8f3b78f1a60..6e47b7bf724 100644 --- a/src/librbd/image/OpenRequest.cc +++ b/src/librbd/image/OpenRequest.cc @@ -370,6 +370,50 @@ Context *OpenRequest<I>::handle_v2_get_create_timestamp(int *result) { return nullptr; } + send_v2_get_access_modify_timestamp(); + return nullptr; +} + +template <typename I> +void OpenRequest<I>::send_v2_get_access_modify_timestamp() { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << dendl; + + librados::ObjectReadOperation op; + cls_client::get_access_timestamp_start(&op); + cls_client::get_modify_timestamp_start(&op); + //TODO: merge w/ create timestamp query after luminous EOLed + + using klass = OpenRequest<I>; + librados::AioCompletion *comp = create_rados_callback< + klass, &klass::handle_v2_get_access_modify_timestamp>(this); + m_out_bl.clear(); + m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, comp, &op, + &m_out_bl); + comp->release(); +} + +template <typename I> +Context *OpenRequest<I>::handle_v2_get_access_modify_timestamp(int *result) { + CephContext *cct = m_image_ctx->cct; + ldout(cct, 10) << this << " " << __func__ << ": r=" << *result << dendl; + + if (*result == 0) { + auto it = m_out_bl.cbegin(); + *result = cls_client::get_access_timestamp_finish(&it, + &m_image_ctx->access_timestamp); + if (*result == 0) + *result = cls_client::get_modify_timestamp_finish(&it, + &m_image_ctx->modify_timestamp); + } + if (*result < 0 && *result != -EOPNOTSUPP) { + lderr(cct) << "failed to retrieve access/modify_timestamp: " + << cpp_strerror(*result) + << dendl; + send_close_image(*result); + return nullptr; + } + send_v2_get_data_pool(); return nullptr; } diff --git a/src/librbd/image/OpenRequest.h b/src/librbd/image/OpenRequest.h index b3bc26a8d47..3476832aac4 100644 --- a/src/librbd/image/OpenRequest.h +++ b/src/librbd/image/OpenRequest.h @@ -55,6 +55,9 @@ private: * V2_GET_CREATE_TIMESTAMP | * | | * v | + * V2_GET_ACCESS_MODIFIY_TIMESTAMP | + * | | + * v | * V2_GET_DATA_POOL --------------> REFRESH * | * v @@ -108,6 +111,9 @@ private: void send_v2_get_create_timestamp(); Context *handle_v2_get_create_timestamp(int *result); + void send_v2_get_access_modify_timestamp(); + Context *handle_v2_get_access_modify_timestamp(int *result); + void send_v2_get_data_pool(); Context *handle_v2_get_data_pool(int *result); diff --git a/src/librbd/io/ImageRequest.cc b/src/librbd/io/ImageRequest.cc index f7964dc0a5c..4da8595cc2e 100644 --- a/src/librbd/io/ImageRequest.cc +++ b/src/librbd/io/ImageRequest.cc @@ -29,6 +29,46 @@ namespace io { using librbd::util::get_image_ctx; +namespace { + +template <typename I> +struct C_UpdateTimestamp : public Context { +public: + I* m_image_ctx; + AioCompletion* m_aio_comp; + bool modify; //if modify set to 'true', modify timestamp is updated, access timestamp otherwise + + C_UpdateTimestamp(I* ictx, AioCompletion *c, bool m) + : m_image_ctx(ictx), m_aio_comp(c), modify(m) { + m_aio_comp->add_request(); + } + + void send() { + librados::ObjectWriteOperation op; + if(modify) + cls_client::set_modify_timestamp(&op); + else + cls_client::set_access_timestamp(&op); + + librados::AioCompletion *comp = librbd::util::create_rados_callback(this); + int r = m_image_ctx->md_ctx.aio_operate(m_image_ctx->header_oid, comp, &op); + assert(r == 0); + comp->release(); + } + + void finish(int r) override { + // ignore errors updating timestamp + m_aio_comp->complete_request(0); + } +}; + +bool should_update_timestamp(const utime_t& now, const utime_t& timestamp, + uint64_t interval) { + return (interval && (static_cast<uint64_t>(now.sec()) >= interval + timestamp)); +} + +} // anonymous namespace + template <typename I> void ImageRequest<I>::aio_read(I *ictx, AioCompletion *c, Extents &&image_extents, @@ -198,7 +238,28 @@ void ImageReadRequest<I>::send_request() { for (auto &object_extent : object_extents) { request_count += object_extent.second.size(); } - aio_comp->set_request_count(request_count); + + utime_t ts = ceph_clock_now(); + + image_ctx.timestamp_lock.get_read(); + if(should_update_timestamp(ts,image_ctx.get_access_timestamp(), + image_ctx.atime_update_interval)) { + image_ctx.timestamp_lock.put_read(); + image_ctx.timestamp_lock.get_write(); + if(should_update_timestamp(ts,image_ctx.get_access_timestamp(), + image_ctx.atime_update_interval)) { + aio_comp->set_request_count(request_count + 1); + auto update_ctx = new C_UpdateTimestamp<I>(&image_ctx, aio_comp, false); + update_ctx->send(); + image_ctx.set_access_timestamp(ts); + } else { + aio_comp->set_request_count(request_count); + } + image_ctx.timestamp_lock.put_write(); + } else { + image_ctx.timestamp_lock.put_read(); + aio_comp->set_request_count(request_count); + } // issue the requests for (auto &object_extent : object_extents) { @@ -285,13 +346,34 @@ void AbstractImageWriteRequest<I>::send_request() { if (!object_extents.empty()) { uint64_t journal_tid = 0; + + utime_t ts = ceph_clock_now(); + image_ctx.timestamp_lock.get_read(); + if(should_update_timestamp(ts,image_ctx.get_modify_timestamp(), + image_ctx.mtime_update_interval)) { + image_ctx.timestamp_lock.put_read(); + image_ctx.timestamp_lock.get_write(); + if(should_update_timestamp(ts,image_ctx.get_modify_timestamp(), + image_ctx.mtime_update_interval)) { + aio_comp->set_request_count(object_extents.size() + 1); + auto update_ctx = new C_UpdateTimestamp<I>(&image_ctx, aio_comp, true); + update_ctx->send(); + image_ctx.set_modify_timestamp(ts); + } else { + aio_comp->set_request_count(object_extents.size()); + } + image_ctx.timestamp_lock.put_write(); + } else { + image_ctx.timestamp_lock.put_read(); + aio_comp->set_request_count(object_extents.size()); + } + if (journaling) { // in-flight ops are flushed prior to closing the journal assert(image_ctx.journal != NULL); journal_tid = append_journal_event(m_synchronous); } - aio_comp->set_request_count(object_extents.size()); send_object_requests(object_extents, snapc, journal_tid); } else { // no IO to perform -- fire completion diff --git a/src/librbd/librbd.cc b/src/librbd/librbd.cc index eaf705fa609..9616be97f83 100644 --- a/src/librbd/librbd.cc +++ b/src/librbd/librbd.cc @@ -1234,6 +1234,34 @@ namespace librbd { return 0; } + int Image::get_access_timestamp(struct timespec *timestamp) + { + ImageCtx *ictx = (ImageCtx *)ctx; + tracepoint(librbd, get_access_timestamp_enter, ictx, ictx->name.c_str(), + ictx->read_only); + { + RWLock::RLocker timestamp_locker(ictx->timestamp_lock); + utime_t time = ictx->get_access_timestamp(); + time.to_timespec(timestamp); + } + tracepoint(librbd, get_access_timestamp_exit, 0, timestamp); + return 0; + } + + int Image::get_modify_timestamp(struct timespec *timestamp) + { + ImageCtx *ictx = (ImageCtx *)ctx; + tracepoint(librbd, get_modify_timestamp_enter, ictx, ictx->name.c_str(), + ictx->read_only); + { + RWLock::RLocker timestamp_locker(ictx->timestamp_lock); + utime_t time = ictx->get_modify_timestamp(); + time.to_timespec(timestamp); + } + tracepoint(librbd, get_modify_timestamp_exit, 0, timestamp); + return 0; + } + int Image::overlap(uint64_t *overlap) { ImageCtx *ictx = (ImageCtx *)ctx; @@ -1814,6 +1842,7 @@ namespace librbd { tracepoint(librbd, read_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, ofs, len); bufferptr ptr(len); bl.push_back(std::move(ptr)); + int r = ictx->io_work_queue->read(ofs, len, io::ReadResult{&bl}, 0); tracepoint(librbd, read_exit, r); return r; @@ -1826,6 +1855,7 @@ namespace librbd { ictx->read_only, ofs, len, op_flags); bufferptr ptr(len); bl.push_back(std::move(ptr)); + int r = ictx->io_work_queue->read(ofs, len, io::ReadResult{&bl}, op_flags); tracepoint(librbd, read_exit, r); return r; @@ -1837,6 +1867,7 @@ namespace librbd { { ImageCtx *ictx = (ImageCtx *)ctx; tracepoint(librbd, read_iterate_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, ofs, len); + int64_t r = librbd::read_iterate(ictx, ofs, len, cb, arg); tracepoint(librbd, read_iterate_exit, r); return r; @@ -1848,6 +1879,7 @@ namespace librbd { { ImageCtx *ictx = (ImageCtx *)ctx; tracepoint(librbd, read_iterate2_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, ofs, len); + int64_t r = librbd::read_iterate(ictx, ofs, len, cb, arg); if (r > 0) r = 0; @@ -1897,6 +1929,7 @@ namespace librbd { tracepoint(librbd, write_exit, -EINVAL); return -EINVAL; } + int r = ictx->io_work_queue->write(ofs, len, bufferlist{bl}, 0); tracepoint(librbd, write_exit, r); return r; @@ -1911,6 +1944,7 @@ namespace librbd { tracepoint(librbd, write_exit, -EINVAL); return -EINVAL; } + int r = ictx->io_work_queue->write(ofs, len, bufferlist{bl}, op_flags); tracepoint(librbd, write_exit, r); return r; @@ -1976,6 +2010,7 @@ namespace librbd { return r; } + int Image::aio_write(uint64_t off, size_t len, bufferlist& bl, RBD::AioCompletion *c) { @@ -1987,6 +2022,7 @@ namespace librbd { } ictx->io_work_queue->aio_write(get_aio_completion(c), off, len, bufferlist{bl}, 0); + tracepoint(librbd, aio_write_exit, 0); return 0; } @@ -2003,6 +2039,7 @@ namespace librbd { } ictx->io_work_queue->aio_write(get_aio_completion(c), off, len, bufferlist{bl}, op_flags); + tracepoint(librbd, aio_write_exit, 0); return 0; } @@ -2023,6 +2060,7 @@ namespace librbd { tracepoint(librbd, aio_read_enter, ictx, ictx->name.c_str(), ictx->snap_name.c_str(), ictx->read_only, off, len, bl.c_str(), c->pc); ldout(ictx->cct, 10) << "Image::aio_read() buf=" << (void *)bl.c_str() << "~" << (void *)(bl.c_str() + len - 1) << dendl; + ictx->io_work_queue->aio_read(get_aio_completion(c), off, len, io::ReadResult{&bl}, 0); tracepoint(librbd, aio_read_exit, 0); @@ -2037,6 +2075,7 @@ namespace librbd { ictx->read_only, off, len, bl.c_str(), c->pc, op_flags); ldout(ictx->cct, 10) << "Image::aio_read() buf=" << (void *)bl.c_str() << "~" << (void *)(bl.c_str() + len - 1) << dendl; + ictx->io_work_queue->aio_read(get_aio_completion(c), off, len, io::ReadResult{&bl}, op_flags); tracepoint(librbd, aio_read_exit, 0); @@ -3479,6 +3518,31 @@ extern "C" int rbd_get_create_timestamp(rbd_image_t image, return 0; } +extern "C" int rbd_get_access_timestamp(rbd_image_t image, + struct timespec *timestamp) +{ + librbd::ImageCtx *ictx = (librbd::ImageCtx *)image; + tracepoint(librbd, get_access_timestamp_enter, ictx, ictx->name.c_str(), + ictx->read_only); + utime_t time = ictx->get_access_timestamp(); + time.to_timespec(timestamp); + tracepoint(librbd, get_access_timestamp_exit, 0, timestamp); + return 0; +} + +extern "C" int rbd_get_modify_timestamp(rbd_image_t image, + struct timespec *timestamp) +{ + librbd::ImageCtx *ictx = (librbd::ImageCtx *)image; + tracepoint(librbd, get_modify_timestamp_enter, ictx, ictx->name.c_str(), + ictx->read_only); + utime_t time = ictx->get_modify_timestamp(); + time.to_timespec(timestamp); + tracepoint(librbd, get_modify_timestamp_exit, 0, timestamp); + return 0; +} + + extern "C" int rbd_get_overlap(rbd_image_t image, uint64_t *overlap) { librbd::ImageCtx *ictx = (librbd::ImageCtx *)image; diff --git a/src/pybind/rbd/rbd.pyx b/src/pybind/rbd/rbd.pyx index fa406ca8082..e19ecf1b768 100644 --- a/src/pybind/rbd/rbd.pyx +++ b/src/pybind/rbd/rbd.pyx @@ -313,6 +313,8 @@ cdef extern from "rbd/librbd.h" nogil: int rbd_get_stripe_unit(rbd_image_t image, uint64_t *stripe_unit) int rbd_get_stripe_count(rbd_image_t image, uint64_t *stripe_count) int rbd_get_create_timestamp(rbd_image_t image, timespec *timestamp) + int rbd_get_access_timestamp(rbd_image_t image, timespec *timestamp) + int rbd_get_modify_timestamp(rbd_image_t image, timespec *timestamp) int rbd_get_overlap(rbd_image_t image, uint64_t *overlap) int rbd_get_name(rbd_image_t image, char *name, size_t *name_len) int rbd_get_id(rbd_image_t image, char *id, size_t id_len) @@ -2976,6 +2978,30 @@ written." % (self.name, ret, length)) raise make_ex(ret, 'error getting create timestamp for image: %s' % (self.name)) return datetime.utcfromtimestamp(timestamp.tv_sec) + def access_timestamp(self): + """ + Return the access timestamp for the image. + """ + cdef: + timespec timestamp + with nogil: + ret = rbd_get_access_timestamp(self.image, ×tamp) + if ret != 0: + raise make_ex(ret, 'error getting access timestamp for image: %s' % (self.name)) + return datetime.fromtimestamp(timestamp.tv_sec) + + def modify_timestamp(self): + """ + Return the modify timestamp for the image. + """ + cdef: + timespec timestamp + with nogil: + ret = rbd_get_modify_timestamp(self.image, ×tamp) + if ret != 0: + raise make_ex(ret, 'error getting modify timestamp for image: %s' % (self.name)) + return datetime.fromtimestamp(timestamp.tv_sec) + def flatten(self): """ Flatten clone image (copy all blocks from parent to child) diff --git a/src/test/cli-integration/rbd/formatted-output.t b/src/test/cli-integration/rbd/formatted-output.t index 1c3d96f1e81..a1a9ff76b44 100644 --- a/src/test/cli-integration/rbd/formatted-output.t +++ b/src/test/cli-integration/rbd/formatted-output.t @@ -133,8 +133,11 @@ whenever it is run. grep -v to ignore it, but still work on other distros. \top_features: (esc) \tflags: (esc) \tcreate_timestamp:* (glob) + \taccess_timestamp:* (glob) + \tmodify_timestamp:* (glob) $ rbd info bar --format json | python -mjson.tool | sed 's/,$/, /' { + "access_timestamp": "*", (glob) "block_name_prefix": "rbd_data.*", (glob) "create_timestamp": "*", (glob) "features": [ @@ -147,6 +150,7 @@ whenever it is run. grep -v to ignore it, but still work on other distros. "flags": [], "format": 2, "id": "*", (glob) + "modify_timestamp": "*", (glob) "name": "bar", "object_size": 4194304, "objects": 256, @@ -176,6 +180,8 @@ whenever it is run. grep -v to ignore it, but still work on other distros. <op_features></op_features> <flags></flags> <create_timestamp>*</create_timestamp> (glob) + <access_timestamp>*</access_timestamp> (glob) + <modify_timestamp>*</modify_timestamp> (glob) </image> $ rbd info bar@snap rbd image 'bar': @@ -189,9 +195,12 @@ whenever it is run. grep -v to ignore it, but still work on other distros. \top_features: (esc) \tflags: (esc) \tcreate_timestamp:* (glob) + \taccess_timestamp:* (glob) + \tmodify_timestamp:* (glob) \tprotected: True (esc) $ rbd info bar@snap --format json | python -mjson.tool | sed 's/,$/, /' { + "access_timestamp": "*", (glob) "block_name_prefix": "rbd_data.*", (glob) "create_timestamp": "*", (glob) "features": [ @@ -204,6 +213,7 @@ whenever it is run. grep -v to ignore it, but still work on other distros. "flags": [], "format": 2, "id": "*", (glob) + "modify_timestamp": "*", (glob) "name": "bar", "object_size": 4194304, "objects": 128, @@ -234,6 +244,8 @@ whenever it is run. grep -v to ignore it, but still work on other distros. <op_features></op_features> <flags></flags> <create_timestamp>*</create_timestamp> (glob) + <access_timestamp>*</access_timestamp> (glob) + <modify_timestamp>*</modify_timestamp> (glob) <protected>true</protected> </image> $ rbd info bar@snap2 @@ -248,9 +260,12 @@ whenever it is run. grep -v to ignore it, but still work on other distros. \top_features: (esc) \tflags: (esc) \tcreate_timestamp:* (glob) + \taccess_timestamp:* (glob) + \tmodify_timestamp:* (glob) \tprotected: False (esc) $ rbd info bar@snap2 --format json | python -mjson.tool | sed 's/,$/, /' { + "access_timestamp": "*", (glob) "block_name_prefix": "rbd_data.*", (glob) "create_timestamp": "*", (glob) "features": [ @@ -263,6 +278,7 @@ whenever it is run. grep -v to ignore it, but still work on other distros. "flags": [], "format": 2, "id": "*", (glob) + "modify_timestamp": "*", (glob) "name": "bar", "object_size": 4194304, "objects": 256, @@ -293,6 +309,8 @@ whenever it is run. grep -v to ignore it, but still work on other distros. <op_features></op_features> <flags></flags> <create_timestamp>*</create_timestamp> (glob) + <access_timestamp>*</access_timestamp> (glob) + <modify_timestamp>*</modify_timestamp> (glob) <protected>false</protected> </image> $ rbd info baz @@ -307,8 +325,11 @@ whenever it is run. grep -v to ignore it, but still work on other distros. \top_features: (esc) \tflags: (esc) \tcreate_timestamp:* (glob) + \taccess_timestamp:* (glob) + \tmodify_timestamp:* (glob) $ rbd info baz --format json | python -mjson.tool | sed 's/,$/, /' { + "access_timestamp": "*", (glob) "block_name_prefix": "rbd_data.*", (glob) "create_timestamp": "*", (glob) "features": [ @@ -317,6 +338,7 @@ whenever it is run. grep -v to ignore it, but still work on other distros. "flags": [], "format": 2, "id": "*", (glob) + "modify_timestamp": "*", (glob) "name": "baz", "object_size": 4194304, "objects": 512, @@ -342,6 +364,8 @@ whenever it is run. grep -v to ignore it, but still work on other distros. <op_features></op_features> <flags></flags> <create_timestamp>*</create_timestamp> (glob) + <access_timestamp>*</access_timestamp> (glob) + <modify_timestamp>*</modify_timestamp> (glob) </image> $ rbd info quux rbd image 'quux': @@ -386,8 +410,11 @@ whenever it is run. grep -v to ignore it, but still work on other distros. \top_features: (esc) \tflags: (esc) \tcreate_timestamp:* (glob) + \taccess_timestamp:* (glob) + \tmodify_timestamp:* (glob) $ rbd info rbd_other/child --format json | python -mjson.tool | sed 's/,$/, /' { + "access_timestamp": "*", (glob) "block_name_prefix": "rbd_data.*", (glob) "create_timestamp": "*", (glob) "features": [ @@ -399,6 +426,7 @@ whenever it is run. grep -v to ignore it, but still work on other distros. "flags": [], "format": 2, "id": "*", (glob) + "modify_timestamp": "*", (glob) "name": "child", "object_size": 4194304, "objects": 128, @@ -427,6 +455,8 @@ whenever it is run. grep -v to ignore it, but still work on other distros. <op_features></op_features> <flags></flags> <create_timestamp>*</create_timestamp> (glob) + <access_timestamp>*</access_timestamp> (glob) + <modify_timestamp>*</modify_timestamp> (glob) </image> $ rbd info rbd_other/child@snap rbd image 'child': @@ -440,11 +470,14 @@ whenever it is run. grep -v to ignore it, but still work on other distros. \top_features: (esc) \tflags: (esc) \tcreate_timestamp:* (glob) + \taccess_timestamp:* (glob) + \tmodify_timestamp:* (glob) \tprotected: False (esc) \tparent: rbd/bar@snap (esc) \toverlap: 512 MiB (esc) $ rbd info rbd_other/child@snap --format json | python -mjson.tool | sed 's/,$/, /' { + "access_timestamp": "*", (glob) "block_name_prefix": "rbd_data.*", (glob) "create_timestamp": "*", (glob) "features": [ @@ -456,6 +489,7 @@ whenever it is run. grep -v to ignore it, but still work on other distros. "flags": [], "format": 2, "id": "*", (glob) + "modify_timestamp": "*", (glob) "name": "child", "object_size": 4194304, "objects": 128, @@ -491,6 +525,8 @@ whenever it is run. grep -v to ignore it, but still work on other distros. <op_features></op_features> <flags></flags> <create_timestamp>*</create_timestamp> (glob) + <access_timestamp>*</access_timestamp> (glob) + <modify_timestamp>*</modify_timestamp> (glob) <protected>false</protected> <parent> <pool>rbd</pool> @@ -511,8 +547,11 @@ whenever it is run. grep -v to ignore it, but still work on other distros. \top_features: (esc) \tflags: (esc) \tcreate_timestamp:* (glob) + \taccess_timestamp:* (glob) + \tmodify_timestamp:* (glob) $ rbd info rbd_other/deep-flatten-child --format json | python -mjson.tool | sed 's/,$/, /' { + "access_timestamp": "*", (glob) "block_name_prefix": "rbd_data.*", (glob) "create_timestamp": "*", (glob) "features": [ @@ -525,6 +564,7 @@ whenever it is run. grep -v to ignore it, but still work on other distros. "flags": [], "format": 2, "id": "*", (glob) + "modify_timestamp": "*", (glob) "name": "deep-flatten-child", "object_size": 4194304, "objects": 128, @@ -554,6 +594,8 @@ whenever it is run. grep -v to ignore it, but still work on other distros. <op_features></op_features> <flags></flags> <create_timestamp>*</create_timestamp> (glob) + <access_timestamp>*</access_timestamp> (glob) + <modify_timestamp>*</modify_timestamp> (glob) </image> $ rbd info rbd_other/deep-flatten-child@snap rbd image 'deep-flatten-child': @@ -567,9 +609,12 @@ whenever it is run. grep -v to ignore it, but still work on other distros. \top_features: (esc) \tflags: (esc) \tcreate_timestamp:* (glob) + \taccess_timestamp:* (glob) + \tmodify_timestamp:* (glob) \tprotected: False (esc) $ rbd info rbd_other/deep-flatten-child@snap --format json | python -mjson.tool | sed 's/,$/, /' { + "access_timestamp": "*", (glob) "block_name_prefix": "rbd_data.*", (glob) "create_timestamp": "*", (glob) "features": [ @@ -582,6 +627,7 @@ whenever it is run. grep -v to ignore it, but still work on other distros. "flags": [], "format": 2, "id": "*", (glob) + "modify_timestamp": "*", (glob) "name": "deep-flatten-child", "object_size": 4194304, "objects": 128, @@ -612,6 +658,8 @@ whenever it is run. grep -v to ignore it, but still work on other distros. <op_features></op_features> <flags></flags> <create_timestamp>*</create_timestamp> (glob) + <access_timestamp>*</access_timestamp> (glob) + <modify_timestamp>*</modify_timestamp> (glob) <protected>false</protected> </image> $ rbd list diff --git a/src/test/cls_rbd/test_cls_rbd.cc b/src/test/cls_rbd/test_cls_rbd.cc index 30e0c2881e6..7eac1593ec8 100644 --- a/src/test/cls_rbd/test_cls_rbd.cc +++ b/src/test/cls_rbd/test_cls_rbd.cc @@ -452,6 +452,34 @@ TEST_F(TestClsRbd, get_create_timestamp) ioctx.close(); } +TEST_F(TestClsRbd, get_access_timestamp) +{ + librados::IoCtx ioctx; + ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx)); + + string oid = get_temp_image_name(); + ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1)); + + utime_t timestamp; + ASSERT_EQ(0, get_access_timestamp(&ioctx, oid, ×tamp)); + ASSERT_LT(0U, timestamp.tv.tv_sec); + + ioctx.close(); +} +TEST_F(TestClsRbd, get_modify_timestamp) +{ + librados::IoCtx ioctx; + ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx)); + + string oid = get_temp_image_name(); + ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1)); + + utime_t timestamp; + ASSERT_EQ(0, get_modify_timestamp(&ioctx, oid, ×tamp)); + ASSERT_LT(0U, timestamp.tv.tv_sec); + + ioctx.close(); +} TEST_F(TestClsRbd, get_data_pool) { librados::IoCtx ioctx; diff --git a/src/test/librbd/io/test_mock_ImageRequest.cc b/src/test/librbd/io/test_mock_ImageRequest.cc index a785de2dbaa..17b3b4943d4 100644 --- a/src/test/librbd/io/test_mock_ImageRequest.cc +++ b/src/test/librbd/io/test_mock_ImageRequest.cc @@ -59,9 +59,11 @@ using ::testing::Invoke; using ::testing::Return; using ::testing::WithArg; using ::testing::WithoutArgs; +using ::testing::Exactly; struct TestMockIoImageRequest : public TestMockFixture { typedef ImageRequest<librbd::MockTestImageCtx> MockImageRequest; + typedef ImageReadRequest<librbd::MockTestImageCtx> MockImageReadRequest; typedef ImageWriteRequest<librbd::MockTestImageCtx> MockImageWriteRequest; typedef ImageDiscardRequest<librbd::MockTestImageCtx> MockImageDiscardRequest; typedef ImageFlushRequest<librbd::MockTestImageCtx> MockImageFlushRequest; @@ -88,6 +90,118 @@ struct TestMockIoImageRequest : public TestMockFixture { } }; +TEST_F(TestMockIoImageRequest, AioWriteModifyTimestamp) { + REQUIRE_FORMAT_V2(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockTestJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + mock_image_ctx.mtime_update_interval = 5; + + utime_t dummy = ceph_clock_now(); + dummy -= utime_t(10,0); + + EXPECT_CALL(mock_image_ctx, get_modify_timestamp()) + .Times(Exactly(3)) + .WillOnce(Return(dummy)) + .WillOnce(Return(dummy)) + .WillOnce(Return(dummy + utime_t(10,0))); + + EXPECT_CALL(mock_image_ctx, set_modify_timestamp(_)) + .Times(Exactly(1)); + + InSequence seq; + expect_is_journal_appending(mock_journal, false); + expect_object_request_send(mock_image_ctx, 0); + + C_SaferCond aio_comp_ctx_1, aio_comp_ctx_2; + AioCompletion *aio_comp_1 = AioCompletion::create_and_start( + &aio_comp_ctx_1, ictx, AIO_TYPE_WRITE); + + AioCompletion *aio_comp_2 = AioCompletion::create_and_start( + &aio_comp_ctx_2, ictx, AIO_TYPE_WRITE); + + bufferlist bl; + bl.append("1"); + MockImageWriteRequest mock_aio_image_write_1(mock_image_ctx, aio_comp_1, + {{0, 1}}, std::move(bl), 0, {}); + { + RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); + mock_aio_image_write_1.send(); + } + ASSERT_EQ(0, aio_comp_ctx_1.wait()); + + expect_is_journal_appending(mock_journal, false); + expect_object_request_send(mock_image_ctx, 0); + + bl.append("1"); + MockImageWriteRequest mock_aio_image_write_2(mock_image_ctx, aio_comp_2, + {{0, 1}}, std::move(bl), 0, {}); + { + RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); + mock_aio_image_write_2.send(); + } + ASSERT_EQ(0, aio_comp_ctx_2.wait()); +} + +TEST_F(TestMockIoImageRequest, AioReadAccessTimestamp) { + REQUIRE_FORMAT_V2(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockTestJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + mock_image_ctx.atime_update_interval = 5; + + utime_t dummy = ceph_clock_now(); + dummy -= utime_t(10,0); + + EXPECT_CALL(mock_image_ctx, get_access_timestamp()) + .Times(Exactly(3)) + .WillOnce(Return(dummy)) + .WillOnce(Return(dummy)) + .WillOnce(Return(dummy + utime_t(10,0))); + + EXPECT_CALL(mock_image_ctx, set_access_timestamp(_)) + .Times(Exactly(1)); + + InSequence seq; + expect_object_request_send(mock_image_ctx, 0); + + C_SaferCond aio_comp_ctx_1, aio_comp_ctx_2; + AioCompletion *aio_comp_1 = AioCompletion::create_and_start( + &aio_comp_ctx_1, ictx, AIO_TYPE_READ); + + + ReadResult rr; + MockImageReadRequest mock_aio_image_read_1(mock_image_ctx, aio_comp_1, + {{0, 1}}, std::move(rr), 0, {}); + { + RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); + mock_aio_image_read_1.send(); + } + ASSERT_EQ(1, aio_comp_ctx_1.wait()); + + AioCompletion *aio_comp_2 = AioCompletion::create_and_start( + &aio_comp_ctx_2, ictx, AIO_TYPE_READ); + expect_object_request_send(mock_image_ctx, 0); + + MockImageReadRequest mock_aio_image_read_2(mock_image_ctx, aio_comp_2, + {{0, 1}}, std::move(rr), 0, {}); + { + RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); + mock_aio_image_read_2.send(); + } + ASSERT_EQ(1, aio_comp_ctx_2.wait()); +} + TEST_F(TestMockIoImageRequest, AioWriteJournalAppendDisabled) { REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); diff --git a/src/test/librbd/mock/MockImageCtx.h b/src/test/librbd/mock/MockImageCtx.h index 99125053c62..f123f41a614 100644 --- a/src/test/librbd/mock/MockImageCtx.h +++ b/src/test/librbd/mock/MockImageCtx.h @@ -61,6 +61,7 @@ struct MockImageCtx { owner_lock(image_ctx.owner_lock), md_lock(image_ctx.md_lock), snap_lock(image_ctx.snap_lock), + timestamp_lock(image_ctx.timestamp_lock), parent_lock(image_ctx.parent_lock), object_map_lock(image_ctx.object_map_lock), async_ops_lock(image_ctx.async_ops_lock), @@ -112,7 +113,9 @@ struct MockImageCtx { non_blocking_aio(image_ctx.non_blocking_aio), blkin_trace_all(image_ctx.blkin_trace_all), enable_alloc_hint(image_ctx.enable_alloc_hint), - ignore_migrating(image_ctx.ignore_migrating) + ignore_migrating(image_ctx.ignore_migrating), + mtime_update_interval(image_ctx.mtime_update_interval), + atime_update_interval(image_ctx.atime_update_interval) { md_ctx.dup(image_ctx.md_ctx); data_ctx.dup(image_ctx.data_ctx); @@ -179,6 +182,13 @@ struct MockImageCtx { MOCK_CONST_METHOD2(is_snap_unprotected, int(librados::snap_t in_snap_id, bool *is_unprotected)); + MOCK_CONST_METHOD0(get_create_timestamp, utime_t()); + MOCK_CONST_METHOD0(get_access_timestamp, utime_t()); + MOCK_CONST_METHOD0(get_modify_timestamp, utime_t()); + + MOCK_METHOD1(set_access_timestamp, void(const utime_t at)); + MOCK_METHOD1(set_modify_timestamp, void(const utime_t at)); + MOCK_METHOD8(add_snap, void(cls::rbd::SnapshotNamespace in_snap_namespace, std::string in_snap_name, librados::snap_t id, @@ -249,6 +259,7 @@ struct MockImageCtx { RWLock &owner_lock; RWLock &md_lock; RWLock &snap_lock; + RWLock ×tamp_lock; RWLock &parent_lock; RWLock &object_map_lock; Mutex &async_ops_lock; @@ -321,6 +332,9 @@ struct MockImageCtx { bool blkin_trace_all; bool enable_alloc_hint; bool ignore_migrating; + uint64_t mtime_update_interval; + uint64_t atime_update_interval; + bool cache; }; } // namespace librbd diff --git a/src/test/librbd/test_librbd.cc b/src/test/librbd/test_librbd.cc index fd2283328fc..6d33b28393b 100644 --- a/src/test/librbd/test_librbd.cc +++ b/src/test/librbd/test_librbd.cc @@ -1897,11 +1897,11 @@ TEST_F(TestLibRBD, TestIO) rbd_image_t image; int order = 0; std::string name = get_temp_image_name(); - uint64_t size = 2 << 20; + uint64_t size = 2 << 20; ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order)); ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL)); - + char test_data[TEST_IO_SIZE + 1]; char zero_data[TEST_IO_SIZE + 1]; char mismatch_data[TEST_IO_SIZE + 1]; @@ -6752,7 +6752,6 @@ TEST_F(TestLibRBD, Namespaces) { ASSERT_EQ(2U, cpp_names.size()); ASSERT_EQ("name1", cpp_names[0]); ASSERT_EQ("name3", cpp_names[1]); - rados_ioctx_destroy(ioctx); } @@ -6947,6 +6946,55 @@ TEST_F(TestLibRBD, MigrationPP) { ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL)); } +TEST_F(TestLibRBD, TestGetAccessTimestamp) +{ + REQUIRE_FORMAT_V2(); + + rados_ioctx_t ioctx; + rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx); + + rbd_image_t image; + int order = 0; + std::string name = get_temp_image_name(); + uint64_t size = 2 << 20; + struct timespec timestamp; + + ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order)); + ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL)); + + ASSERT_EQ(0, rbd_get_access_timestamp(image, ×tamp)); + ASSERT_LT(0, timestamp.tv_sec); + + ASSERT_PASSED(validate_object_map, image); + ASSERT_EQ(0, rbd_close(image)); + + rados_ioctx_destroy(ioctx); +} + +TEST_F(TestLibRBD, TestGetModifyTimestamp) +{ + REQUIRE_FORMAT_V2(); + + rados_ioctx_t ioctx; + rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx); + + rbd_image_t image; + int order = 0; + std::string name = get_temp_image_name(); + uint64_t size = 2 << 20; + struct timespec timestamp; + + ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order)); + ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL)); + ASSERT_EQ(0, rbd_get_modify_timestamp(image, ×tamp)); + ASSERT_LT(0, timestamp.tv_sec); + + ASSERT_PASSED(validate_object_map, image); + ASSERT_EQ(0, rbd_close(image)); + + rados_ioctx_destroy(ioctx); +} + // poorman's assert() namespace ceph { void __ceph_assert_fail(const char *assertion, const char *file, int line, diff --git a/src/test/pybind/test_rbd.py b/src/test/pybind/test_rbd.py index 38cd58bfd3a..cf55041ae1c 100644 --- a/src/test/pybind/test_rbd.py +++ b/src/test/pybind/test_rbd.py @@ -384,6 +384,16 @@ class TestImage(object): assert_not_equal(0, timestamp.year) assert_not_equal(1970, timestamp.year) + def test_access_timestamp(self): + timestamp = self.image.access_timestamp() + assert_not_equal(0, timestamp.year) + assert_not_equal(1970, timestamp.year) + + def test_modify_timestamp(self): + timestamp = self.image.modify_timestamp() + assert_not_equal(0, timestamp.year) + assert_not_equal(1970, timestamp.year) + def test_invalidate_cache(self): self.image.write(b'abc', 0) eq(b'abc', self.image.read(0, 3)) diff --git a/src/tools/rbd/action/Info.cc b/src/tools/rbd/action/Info.cc index df8b09aabc6..4192a246ef0 100644 --- a/src/tools/rbd/action/Info.cc +++ b/src/tools/rbd/action/Info.cc @@ -11,6 +11,8 @@ #include <iostream> #include <boost/program_options.hpp> +#include "common/Clock.h" + namespace rbd { namespace action { namespace info { @@ -74,6 +76,14 @@ static void format_flags(Formatter *f, uint64_t flags) format_bitmask(f, "flag", mapping, flags); } +void format_timestamp(struct timespec timestamp, std::string ×tamp_str) { + if(timestamp.tv_sec != 0) { + time_t ts = timestamp.tv_sec; + timestamp_str = ctime(&ts); + timestamp_str = timestamp_str.substr(0, timestamp_str.length() - 1); + } +} + static int do_show_info(librados::IoCtx &io_ctx, librbd::Image& image, const std::string &snapname, Formatter *f) { @@ -190,13 +200,20 @@ static int do_show_info(librados::IoCtx &io_ctx, librbd::Image& image, struct timespec create_timestamp; image.get_create_timestamp(&create_timestamp); - string create_timestamp_str = ""; - if(create_timestamp.tv_sec != 0) { - time_t timestamp = create_timestamp.tv_sec; - create_timestamp_str = ctime(×tamp); - create_timestamp_str = create_timestamp_str.substr(0, - create_timestamp_str.length() - 1); - } + std::string create_timestamp_str = ""; + format_timestamp(create_timestamp, create_timestamp_str); + + struct timespec access_timestamp; + image.get_access_timestamp(&access_timestamp); + + std::string access_timestamp_str = ""; + format_timestamp(access_timestamp, access_timestamp_str); + + struct timespec modify_timestamp; + image.get_modify_timestamp(&modify_timestamp); + + std::string modify_timestamp_str = ""; + format_timestamp(modify_timestamp, modify_timestamp_str); if (f) { f->open_object_section("image"); @@ -258,6 +275,24 @@ static int do_show_info(librados::IoCtx &io_ctx, librbd::Image& image, } } + if (!access_timestamp_str.empty()) { + if (f) { + f->dump_string("access_timestamp", access_timestamp_str); + } else { + std::cout << "\taccess_timestamp: " << access_timestamp_str + << std::endl; + } + } + + if (!modify_timestamp_str.empty()) { + if (f) { + f->dump_string("modify_timestamp", modify_timestamp_str); + } else { + std::cout << "\tmodify_timestamp: " << modify_timestamp_str + << std::endl; + } + } + // snapshot info, if present if (!snapname.empty()) { if (f) { diff --git a/src/tracing/librbd.tp b/src/tracing/librbd.tp index e334e018cde..3c189fb33f0 100644 --- a/src/tracing/librbd.tp +++ b/src/tracing/librbd.tp @@ -2159,6 +2159,51 @@ TRACEPOINT_EVENT(librbd, get_create_timestamp_exit, ) ) +TRACEPOINT_EVENT(librbd, get_access_timestamp_enter, + TP_ARGS( + void*, imagectx, + const char*, name, + char, read_only), + TP_FIELDS( + ctf_integer_hex(void*, imagectx, imagectx) + ctf_string(name, name) + ctf_integer(char, read_only, read_only) + ) +) + +TRACEPOINT_EVENT(librbd, get_access_timestamp_exit, + TP_ARGS( + int, retval, + struct timespec*, timestamp), + TP_FIELDS( + ctf_integer(int, retval, retval) + ctf_integer(uint64_t, timestamp, timestamp->tv_sec) + ) +) + +TRACEPOINT_EVENT(librbd, get_modify_timestamp_enter, + TP_ARGS( + void*, imagectx, + const char*, name, + char, read_only), + TP_FIELDS( + ctf_integer_hex(void*, imagectx, imagectx) + ctf_string(name, name) + ctf_integer(char, read_only, read_only) + ) +) + +TRACEPOINT_EVENT(librbd, get_modify_timestamp_exit, + TP_ARGS( + int, retval, + struct timespec*, timestamp), + TP_FIELDS( + ctf_integer(int, retval, retval) + ctf_integer(uint64_t, timestamp, timestamp->tv_sec) + ) +) + + TRACEPOINT_EVENT(librbd, get_features_enter, TP_ARGS( void*, imagectx, |