]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: add image access/last modified timestamps
authorJulien Collet <julien.collet@cern.ch>
Tue, 20 Feb 2018 09:14:00 +0000 (10:14 +0100)
committerJason Dillaman <dillaman@redhat.com>
Tue, 21 Aug 2018 20:17:17 +0000 (16:17 -0400)
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>
21 files changed:
src/cls/rbd/cls_rbd.cc
src/cls/rbd/cls_rbd_client.cc
src/cls/rbd/cls_rbd_client.h
src/common/options.cc
src/include/rbd/librbd.h
src/include/rbd/librbd.hpp
src/librbd/ImageCtx.cc
src/librbd/ImageCtx.h
src/librbd/image/OpenRequest.cc
src/librbd/image/OpenRequest.h
src/librbd/io/ImageRequest.cc
src/librbd/librbd.cc
src/pybind/rbd/rbd.pyx
src/test/cli-integration/rbd/formatted-output.t
src/test/cls_rbd/test_cls_rbd.cc
src/test/librbd/io/test_mock_ImageRequest.cc
src/test/librbd/mock/MockImageCtx.h
src/test/librbd/test_librbd.cc
src/test/pybind/test_rbd.py
src/tools/rbd/action/Info.cc
src/tracing/librbd.tp

index b35795796940679d7fa341647f3ad1a4134849cc..ed2163dbf4c431128707ac35670e08c71fd6f446 100644 (file)
@@ -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");
@@ -1093,6 +1095,81 @@ int get_create_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *
   return 0;
 }
 
+/**
+ * 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
  *
@@ -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,
index 087ed597b23b19ba8d1212499d08ba4dbca454a4..88317f893bc3de0ab302f706cbf755c0e7596676 100644 (file)
@@ -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) {
index 2acc5be47437ad659b777fc48b62fb69da5d1df3..c9c362bb53689328cbc25cb50d60478f5ab26f48 100644 (file)
@@ -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);
index 7d52edd0a47fc8136a3d3845424402c7dea9f489..ef57318c92bb4f251a35da9e42d69250ef25d565 100644 (file)
@@ -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."),
   });
 }
 
index 045c0dfe15b9f87e1802503b377d92ee788906e3..66d5444e943ac41574fd9f2bab5245b94aa5f4a7 100644 (file)
@@ -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);
index 62b3081f750e36ef2a4b66112ad64269abecdfd7..c6270d33109ad85f8376a6ea1c060d5b13e21a36 100644 (file)
@@ -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);
index 3abb4f677eb8bca2c2b8bfd91fc68f1aec9e944f..1552829d2e0b9361053023465f02359352f7e473 100644 (file)
@@ -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);
index 259227870e3ab20c303a736e828f90dd0ec49252..f623baffb65c8c81bcb890ded92823c169543421 100644 (file)
@@ -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,
index 8f3b78f1a60ea4d438d3c75bf5d403b176f4dbb5..6e47b7bf724dbb2c7d358a06db68b102097f5824 100644 (file)
@@ -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;
 }
index b3bc26a8d47c070106745cfdee0a785cb642258d..3476832aac4fb628a158286f8bbe49374233fa0e 100644 (file)
@@ -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);
 
index f7964dc0a5caac05a72754bb7624dc8964ff54f3..4da8595cc2eaac17b2f629a8900098196dba519b 100644 (file)
@@ -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
index eaf705fa6097fb2a640540081a7cb2a176ef35bf..9616be97f8386460bc292ed548168e1c031060b4 100644 (file)
@@ -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;
index fa406ca80825b77dc6bc815b2385324d7bc65902..e19ecf1b7685cfd6040105756fcb3b226b98971a 100644 (file)
@@ -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)
index 1c3d96f1e810589ae98cb020f18f6dcb66083ce2..a1a9ff76b44f68ca358089e4ca47841108e1a75b 100644 (file)
@@ -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
index 30e0c2881e6f0328c1546907c170ebee0af1551b..7eac1593ec817e641edb1ee7d6a134ff8bd05d9b 100644 (file)
@@ -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;
index a785de2dbaa63879dfdc6356bfdfa527b5ebcdae..17b3b4943d4adf24f23872ea74e216b3281d6e61 100644 (file)
@@ -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);
 
index 99125053c62a40330e13b062bdc04f7ca4d30583..f123f41a6147853acd8c065020e7cd367bdfb90e 100644 (file)
@@ -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
index fd2283328fceb29851ef7c9aa4ec963f0b9ebc41..6d33b28393bcda907eb82ff838952a5196c8b61a 100644 (file)
@@ -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,
index 38cd58bfd3a2f0cd301e6e15adcceee2d9c7e2fa..cf55041ae1cf24c2939a0eea4975b26b5cf39f7a 100644 (file)
@@ -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))
index df8b09aabc6bece8961adedd2b6b7e828c6139b0..4192a246ef0c79fc47f2622356edf16f51ebbb45 100644 (file)
@@ -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) {
index e334e018cde74e2e36a83a0e76a7d2b36de757aa..3c189fb33f0d04a46eea89d0d22a9edb49dec484 100644 (file)
@@ -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,