]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librgw: implement intrusive filehandle cache
authorMatt Benjamin <mbenjamin@redhat.com>
Tue, 3 Nov 2015 01:31:53 +0000 (20:31 -0500)
committerMatt Benjamin <mbenjamin@redhat.com>
Fri, 12 Feb 2016 17:05:35 +0000 (12:05 -0500)
The cache is keyed on a hash of bucket name, plus a hash of the
object name.

Signed-off-by: Matt Benjamin <mbenjamin@redhat.com>
src/include/rados/rgw_file.h
src/rgw/rgw_file.cc
src/rgw/rgw_file.h

index a5f4b1199b80c9d0ca7725fa3fcab64cf78aea3b..29a55200648081c1862b47b16c2bad0ad77711ae 100644 (file)
@@ -36,13 +36,17 @@ enum rgw_fh_type {
 /*
  * dynamic allocated handle to support nfs handle
  */
+
+/* content-addressable hash */
+struct rgw_fh_hk {
+  uint64_t bucket;
+  uint64_t object;
+};
+
 struct rgw_file_handle
 {
   /* content-addressable hash */
-  struct {
-    uint64_t bucket;
-    uint64_t object;
-  } fh_hk;
+  struct rgw_fh_hk fh_hk;
   void *fh_private; /* librgw private data */
   /* object type */
   enum rgw_fh_type fh_type;
index 7b92b61eb35838a09399136b6ddf71655583669a..e3f86e1983e5a3b59fbbdad23d2e6757cba04c2a 100644 (file)
 
 #define dout_subsys ceph_subsys_rgw
 
+using namespace rgw;
+
 extern RGWLib librgw;
 
 const string RGWFileHandle::root_name = "/";
 
+atomic<uint32_t> RGWLibFS::fs_inst;
+
 /* librgw */
 extern "C" {
 
@@ -182,7 +186,7 @@ int rgw_lookup(struct rgw_fs *rgw_fs,
     return EINVAL;
   }
 
-  RGWFileHandle* rgw_fh = new RGWFileHandle(fs, parent, path);
+  RGWFileHandle* rgw_fh = fs->lookup_fh(parent, path);
   if (! rgw_fh) {
     /* not found */
     return ENOENT;
index f853d77f0a467440275058372eb84c2fff0c332a..8f2aec5f8fb962a814c8b38402394de03d630fef 100644 (file)
@@ -7,11 +7,14 @@
 #include "include/rados/rgw_file.h"
 
 /* internal header */
+#include <string.h>
 
 #include <atomic>
+#include <mutex>
 #include <boost/intrusive_ptr.hpp>
 #include "xxhash.h"
 #include "include/buffer.h"
+#include "common/cohort_lru.h"
 
 /* XXX
  * ASSERT_H somehow not defined after all the above (which bring
  */
 #include "include/assert.h"
 
+namespace rgw {
 
-class RGWLibFS;
-class RGWFileHandle;
+  namespace bi = boost::intrusive;
 
-typedef boost::intrusive_ptr<RGWFileHandle> RGWFHRef;
+  class RGWLibFS;
+  class RGWFileHandle;
 
-class RGWFileHandle
-{
-  struct rgw_file_handle fh;
-  mutable std::atomic<uint64_t> refcnt;
-  RGWFHRef parent;
-  const string name; /* XXX file or bucket name */
-  uint32_t flags;
+  typedef boost::intrusive_ptr<RGWFileHandle> RGWFHRef;
 
-public:
-  const static string root_name;
-
-  static constexpr uint32_t FLAG_NONE = 0x0000;
-  static constexpr uint32_t FLAG_OPEN = 0x0001;
-  static constexpr uint32_t FLAG_ROOT = 0x0002;
-  
-  RGWFileHandle(RGWLibFS* fs, RGWFileHandle* _parent, const char* _name)
-    : parent(_parent), name(_name), flags(FLAG_NONE) {
-    if (parent) {
-      fh.fh_type = parent->is_root() ?
-       RGW_FS_TYPE_DIRECTORY : RGW_FS_TYPE_FILE;
-      /* content-addressable hash (bucket part) */
-      fh.fh_hk.bucket = parent->get_fh()->fh_hk.object;
-    } else {
-      /* root */
-      fh.fh_type = RGW_FS_TYPE_DIRECTORY;
-      /* XXX give root buckets a locally-unique bucket hash part */
-      fh.fh_hk.bucket = XXH64(reinterpret_cast<char*>(fs), 8, 8675309);
-      flags |= FLAG_ROOT;
+  /*
+   * XXX
+   * The current 64-bit, non-cryptographic hash used here is intended
+   * for prototyping only.
+   *
+   * However, the invariant being prototyped is that objects be
+   * identifiable by their hash components alone.  We believe this can
+   * be legitimately implemented using 128-hash values for bucket and
+   * object components, together with a cluster-resident cryptographic
+   * key.  Since an MD5 or SHA-1 key is 128 bits and the (fast),
+   * non-cryptographic CityHash128 hash algorithm takes a 128-bit seed,
+   * speculatively we could use that for the final hash computations.
+   */
+  struct fh_key
+  {
+    rgw_fh_hk fh_hk;
+
+    static constexpr uint64_t seed = 8675309;
+
+    fh_key() {}
+
+    fh_key(const rgw_fh_hk& _hk)
+      : fh_hk(_hk) {
+      // nothing
     }
-    /* content-addressable hash (object part) */
-    fh.fh_hk.object = XXH64(name.c_str(), name.length(), 8675309 /* XXX */);
-    fh.fh_private = this;
-  }
 
-  struct rgw_file_handle* get_fh() { return &fh; }
+    fh_key(const uint64_t bk, const char *_o) {
+      fh_hk.bucket = bk;
+      fh_hk.object = XXH64(_o, ::strlen(_o), seed);
+    }
+    
+    fh_key(const std::string& _b, const std::string& _o) {
+      fh_hk.bucket = XXH64(_b.c_str(), _o.length(), seed);
+      fh_hk.object = XXH64(_o.c_str(), _o.length(), seed);
+    }
+  }; /* fh_key */
+
+  inline bool operator<(const fh_key& lhs, const fh_key& rhs)
+  {
+    return ((lhs.fh_hk.bucket < rhs.fh_hk.bucket) ||
+           ((lhs.fh_hk.bucket == rhs.fh_hk.bucket) &&
+             (lhs.fh_hk.object < rhs.fh_hk.object)));
+  }
+
+  inline bool operator>(const fh_key& lhs, const fh_key& rhs)
+  {
+    return (rhs < lhs);
+  }
+
+  inline bool operator==(const fh_key& lhs, const fh_key& rhs)
+  {
+    return ((lhs.fh_hk.bucket == rhs.fh_hk.bucket) &&
+           (lhs.fh_hk.object == rhs.fh_hk.object));
+  }
+
+  inline bool operator!=(const fh_key& lhs, const fh_key& rhs)
+  {
+    return !(lhs == rhs);
+  }
+
+  inline bool operator<=(const fh_key& lhs, const fh_key& rhs)
+  {
+    return (lhs < rhs) || (lhs == rhs);
+  }
+
+  class RGWFileHandle
+  {
+    struct rgw_file_handle fh;
+    mutable std::atomic<uint64_t> refcnt;
+    RGWLibFS* fs;
+    RGWFHRef parent;
+    /* const */ std::string name; /* XXX file or bucket name */
+    /* const */ fh_key fhk;
+    uint32_t flags;
+
+  public:
+    const static string root_name;
+
+    static constexpr uint32_t FLAG_NONE = 0x0000;
+    static constexpr uint32_t FLAG_OPEN = 0x0001;
+    static constexpr uint32_t FLAG_ROOT = 0x0002;
+
+    friend class RGWLibFS;
+
+  private:
+    RGWFileHandle(RGWLibFS* _fs)
+      : fs(_fs), parent(nullptr), flags(FLAG_ROOT)
+      {
+       /* root */
+       fh.fh_type = RGW_FS_TYPE_DIRECTORY;
+       /* pointer to self */
+       fh.fh_private = this;
+      }
+    
+    void init_rootfs(std::string& fsid, const std::string& object_name) {
+      /* fh_key */
+      fh.fh_hk.bucket = XXH64(fsid.c_str(), fsid.length(), fh_key::seed);
+      fh.fh_hk.object = XXH64(object_name.c_str(), object_name.length(),
+                             fh_key::seed);
+    }
+
+  public:
+    RGWFileHandle(RGWLibFS* fs, RGWFileHandle* _parent, const fh_key& _fhk,
+                 const char *_name)
+      : parent(_parent), name(_name), fhk(_fhk), flags(FLAG_NONE) {
+
+      fh.fh_type = parent->is_root()
+       ? RGW_FS_TYPE_DIRECTORY : RGW_FS_TYPE_FILE;      
+
+      /* save constant fhk */
+      fh_key fhk(parent->name, name);
+      fh.fh_hk = fhk.fh_hk; /* XXX redundant in fh_hk */
 
-  const std::string& bucket_name() {
-    if (is_root())
-      return root_name;
-    if (is_object()) {
-      return parent->object_name();
+      /* pointer to self */
+      fh.fh_private = this;
     }
-    return name;
-  }
 
-  const std::string& object_name() { return name; }
+    const fh_key& get_key() const {
+      return fhk;
+    }
 
-  bool is_open() const { return flags & FLAG_OPEN; }
-  bool is_root() const { return flags & FLAG_ROOT; }
-  bool is_bucket() const { return (fh.fh_type == RGW_FS_TYPE_DIRECTORY); }
-  bool is_object() const { return (fh.fh_type == RGW_FS_TYPE_FILE); }
+    struct rgw_file_handle* get_fh() { return &fh; }
 
-  void open() {
-    flags |= FLAG_OPEN;
-  }
+    const std::string& bucket_name() const {
+      if (is_root())
+       return root_name;
+      if (is_object()) {
+       return parent->object_name();
+      }
+      return name;
+    }
 
-  void close() {
-    flags &= ~FLAG_OPEN;
-  }
+    const std::string& object_name() const { return name; }
 
-  friend void intrusive_ptr_add_ref(const RGWFileHandle* fh) {
-    fh->refcnt.fetch_add(1, std::memory_order_relaxed);
-  }
+    bool is_open() const { return flags & FLAG_OPEN; }
+    bool is_root() const { return flags & FLAG_ROOT; }
+    bool is_bucket() const { return (fh.fh_type == RGW_FS_TYPE_DIRECTORY); }
+    bool is_object() const { return (fh.fh_type == RGW_FS_TYPE_FILE); }
 
-  friend void intrusive_ptr_release(const RGWFileHandle* fh) {
-    if (fh->refcnt.fetch_sub(1, std::memory_order_release) == 1) {
-      std::atomic_thread_fence(std::memory_order_acquire);
-      /* root handles are expanded in RGWLibFS */
-      if (! const_cast<RGWFileHandle*>(fh)->is_root())
-       delete fh;
+    void open() {
+      flags |= FLAG_OPEN;
     }
-  }
 
-  inline void rele() {
-    intrusive_ptr_release(this);
-  }
-}; /* RGWFileHandle */
+    void close() {
+      flags &= ~FLAG_OPEN;
+    }
 
-static inline RGWFileHandle* get_rgwfh(struct rgw_file_handle* fh) {
-  return static_cast<RGWFileHandle*>(fh->fh_private);
-}
+    friend void intrusive_ptr_add_ref(const RGWFileHandle* fh) {
+      fh->refcnt.fetch_add(1, std::memory_order_relaxed);
+    }
 
-class RGWLibFS
-{
-  struct rgw_fs fs;
-  RGWFileHandle root_fh;
+    friend void intrusive_ptr_release(const RGWFileHandle* fh) {
+      if (fh->refcnt.fetch_sub(1, std::memory_order_release) == 1) {
+       std::atomic_thread_fence(std::memory_order_acquire);
+       /* root handles are expanded in RGWLibFS */
+       if (! const_cast<RGWFileHandle*>(fh)->is_root())
+         delete fh;
+      }
+    }
 
-  std::string uid; // should match user.user_id, iiuc
+    inline void rele() {
+      intrusive_ptr_release(this);
+    }
 
-  RGWUserInfo user;
-  RGWAccessKey key;
+    struct FhLT
+    {
+      // for internal ordering
+      bool operator()(const RGWFileHandle& lhs, const RGWFileHandle& rhs) const
+       { return (lhs.get_key() < rhs.get_key()); }
+
+      // for external search by fh_key
+      bool operator()(const fh_key& k, const RGWFileHandle& fh) const
+       { return k < fh.get_key(); }
+
+      bool operator()(const RGWFileHandle& fh, const fh_key& k) const
+       { return fh.get_key() < k; }
+    };
+
+    struct FhEQ
+    {
+      bool operator()(const RGWFileHandle& lhs, const RGWFileHandle& rhs) const
+       { return (lhs.get_key() == rhs.get_key()); }
+
+      bool operator()(const fh_key& k, const RGWFileHandle& fh) const
+       { return k == fh.get_key(); }
+
+      bool operator()(const RGWFileHandle& fh, const fh_key& k) const
+       { return fh.get_key() == k; }
+    };
+
+    typedef bi::link_mode<bi::safe_link> link_mode; /* XXX normal */
+#if defined(FHCACHE_AVL)
+    typedef bi::avl_set_member_hook<link_mode> tree_hook_type;
+#else
+    /* RBT */
+    typedef bi::set_member_hook<link_mode> tree_hook_type;
+#endif
+    tree_hook_type fh_hook;
+
+    typedef bi::member_hook<
+      RGWFileHandle, tree_hook_type, &RGWFileHandle::fh_hook> FhHook;
+
+#if defined(FHCACHE_AVL)
+    typedef bi::avltree<RGWFileHandle, bi::compare<FhLT>, FhHook> FHTree;
+#else
+    typedef bi::rbtree<RGWFileHandle, bi::compare<FhLT>, FhHook> FhTree;
+#endif
+    typedef cohort::lru::TreeX<RGWFileHandle, FhTree, FhLT, FhEQ, fh_key,
+                              std::mutex> FHCache;
+  }; /* RGWFileHandle */
+
+  static inline RGWFileHandle* get_rgwfh(struct rgw_file_handle* fh) {
+    return static_cast<RGWFileHandle*>(fh->fh_private);
+  }
+
+  class RGWLibFS
+  {
+    struct rgw_fs fs;
+    RGWFileHandle root_fh;
+
+    RGWFileHandle::FHCache fh_cache;
+    
+    std::string uid; // should match user.user_id, iiuc
+
+    RGWUserInfo user;
+    RGWAccessKey key; // XXXX acc_key
+
+    static atomic<uint32_t> fs_inst;
+    std::string fsid;
+    
+  public:
+    RGWLibFS(const char *_uid, const char *_user_id, const char* _key)
+      : root_fh(this),  uid(_uid), key(_user_id, _key) {
+
+      /* no bucket may be named rgw_fs_inst-(.*) */
+      fsid = RGWFileHandle::root_name + "rgw_fs_inst-" +
+       std::to_string(++(fs_inst));
+
+      root_fh.init_rootfs(fsid /* bucket */, RGWFileHandle::root_name);
+
+      /* pointer to self */
+      fs.fs_private = this;
+
+      /* expose public root fh */
+      fs.root_fh = root_fh.get_fh();
+    }
 
-public:
-  RGWLibFS(const char *_uid, const char *_user_id, const char* _key)
-    : root_fh(this, nullptr, RGWFileHandle::root_name.c_str()), uid(_uid),
-      key(_user_id, _key) {
-    fs.fs_private = this;
-    fs.root_fh = root_fh.get_fh(); /* expose public root fh */
-  }
-
-  int authorize(RGWRados* store) {
-    int ret = rgw_get_user_info_by_access_key(store, key.id, user);
-    if (ret == 0) {
-      RGWAccessKey* key0 = user.get_key0();
-      if (!key0 ||
-         (key0->key != key.key))
-       return -EINVAL;
-      if (user.suspended)
-       return -ERR_USER_SUSPENDED;
+    int authorize(RGWRados* store) {
+      int ret = rgw_get_user_info_by_access_key(store, key.id, user);
+      if (ret == 0) {
+       RGWAccessKey* key0 = user.get_key0();
+       if (!key0 ||
+           (key0->key != key.key))
+         return -EINVAL;
+       if (user.suspended)
+         return -ERR_USER_SUSPENDED;
+      }
+      return 0;
     }
-    return 0;
-  }
 
-  struct rgw_fs* get_fs() { return &fs; }
-  RGWUserInfo* get_user() { return &user; }
+    /* find or create an RGWFileHandle */
+    RGWFileHandle* lookup_fh(RGWFileHandle* parent, const char *name) {
+
+      RGWFileHandle::FHCache::Latch lat;
+      fh_key fhk(parent->fhk.fh_hk.object, name);
+
+      RGWFileHandle* fh =
+       fh_cache.find_latch(fhk.fh_hk.object /* partition selector*/,
+                           fhk /* key */, lat /* serializer */,
+                           RGWFileHandle::FHCache::FLAG_LOCK);
+      /* LATCHED */
+      if (! fh) {
+       fh = new RGWFileHandle(this, parent, fhk, name);
+       intrusive_ptr_add_ref(fh); /* sentinel ref */
+       fh_cache.insert_latched(fh, lat,
+                               RGWFileHandle::FHCache::FLAG_NONE);
+      }
+      intrusive_ptr_add_ref(fh); /* call path/handle ref */
+      lat.lock->unlock(); /* !LATCHED */
+      return fh;
+    }
 
-  bool is_root(struct rgw_fs* _fs) {
-    return (&fs == _fs);
-  }
+    struct rgw_fs* get_fs() { return &fs; }
+
+    RGWUserInfo* get_user() { return &user; }
+
+    bool is_root(struct rgw_fs* _fs) {
+      return (&fs == _fs);
+    }
+
+  }; /* RGWLibFS */
 
-}; /* RGWLibFS */
+} /* namespace rgw */
 
 /*
   read directory content (buckets)