summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJulien Collet <julien.collet@cern.ch>2018-02-20 10:14:00 +0100
committerJason Dillaman <dillaman@redhat.com>2018-08-21 22:17:17 +0200
commit809c5430c2929ea1b33d3d1ab7c0023fbb2ce7a5 (patch)
tree7d11af46ef75621e0674697c479e31f4b7f15888 /src
parentMerge pull request #23649 from trociny/wip-26939 (diff)
downloadceph-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.cc156
-rw-r--r--src/cls/rbd/cls_rbd_client.cc92
-rw-r--r--src/cls/rbd/cls_rbd_client.h19
-rw-r--r--src/common/options.cc10
-rw-r--r--src/include/rbd/librbd.h4
-rw-r--r--src/include/rbd/librbd.hpp2
-rw-r--r--src/librbd/ImageCtx.cc27
-rw-r--r--src/librbd/ImageCtx.h12
-rw-r--r--src/librbd/image/OpenRequest.cc44
-rw-r--r--src/librbd/image/OpenRequest.h6
-rw-r--r--src/librbd/io/ImageRequest.cc86
-rw-r--r--src/librbd/librbd.cc64
-rw-r--r--src/pybind/rbd/rbd.pyx26
-rw-r--r--src/test/cli-integration/rbd/formatted-output.t48
-rw-r--r--src/test/cls_rbd/test_cls_rbd.cc28
-rw-r--r--src/test/librbd/io/test_mock_ImageRequest.cc114
-rw-r--r--src/test/librbd/mock/MockImageCtx.h16
-rw-r--r--src/test/librbd/test_librbd.cc54
-rw-r--r--src/test/pybind/test_rbd.py10
-rw-r--r--src/tools/rbd/action/Info.cc49
-rw-r--r--src/tracing/librbd.tp45
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, &timestamp)
+ 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, &timestamp)
+ 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, &timestamp));
+ 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, &timestamp));
+ 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 &timestamp_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, &timestamp));
+ 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, &timestamp));
+ 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 &timestamp_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(&timestamp);
- 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,