initial code repo
[stor4nfv.git] / src / ceph / src / mds / CDir.h
diff --git a/src/ceph/src/mds/CDir.h b/src/ceph/src/mds/CDir.h
new file mode 100644 (file)
index 0000000..45b2998
--- /dev/null
@@ -0,0 +1,754 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software 
+ * Foundation.  See file COPYING.
+ * 
+ */
+
+
+
+#ifndef CEPH_CDIR_H
+#define CEPH_CDIR_H
+
+#include "include/counter.h"
+#include "include/types.h"
+#include "include/buffer_fwd.h"
+#include "common/bloom_filter.hpp"
+#include "common/config.h"
+#include "common/DecayCounter.h"
+
+#include "MDSCacheObject.h"
+
+#include <iosfwd>
+
+#include <list>
+#include <set>
+#include <map>
+#include <string>
+
+
+#include "CInode.h"
+
+class CDentry;
+class MDCache;
+
+struct ObjectOperation;
+
+ostream& operator<<(ostream& out, const class CDir& dir);
+class CDir : public MDSCacheObject, public Counter<CDir> {
+  friend ostream& operator<<(ostream& out, const class CDir& dir);
+
+public:
+  MEMPOOL_CLASS_HELPERS();
+  // -- pins --
+  static const int PIN_DNWAITER =     1;
+  static const int PIN_INOWAITER =    2;
+  static const int PIN_CHILD =        3;
+  static const int PIN_FROZEN =       4;
+  static const int PIN_SUBTREE =      5;
+  static const int PIN_IMPORTING =    7;
+  static const int PIN_IMPORTBOUND =  9;
+  static const int PIN_EXPORTBOUND = 10;
+  static const int PIN_STICKY =      11;
+  static const int PIN_SUBTREETEMP = 12;  // used by MDCache::trim_non_auth()
+  const char *pin_name(int p) const override {
+    switch (p) {
+    case PIN_DNWAITER: return "dnwaiter";
+    case PIN_INOWAITER: return "inowaiter";
+    case PIN_CHILD: return "child";
+    case PIN_FROZEN: return "frozen";
+    case PIN_SUBTREE: return "subtree";
+    case PIN_IMPORTING: return "importing";
+    case PIN_IMPORTBOUND: return "importbound";
+    case PIN_EXPORTBOUND: return "exportbound";
+    case PIN_STICKY: return "sticky";
+    case PIN_SUBTREETEMP: return "subtreetemp";
+    default: return generic_pin_name(p);
+    }
+  }
+
+  // -- state --
+  static const unsigned STATE_COMPLETE =      (1<< 1);   // the complete contents are in cache
+  static const unsigned STATE_FROZENTREE =    (1<< 2);   // root of tree (bounded by exports)
+  static const unsigned STATE_FREEZINGTREE =  (1<< 3);   // in process of freezing 
+  static const unsigned STATE_FROZENDIR =     (1<< 4);
+  static const unsigned STATE_FREEZINGDIR =   (1<< 5);
+  static const unsigned STATE_COMMITTING =    (1<< 6);   // mid-commit
+  static const unsigned STATE_FETCHING =      (1<< 7);   // currenting fetching
+  static const unsigned STATE_CREATING =      (1<< 8);
+  static const unsigned STATE_IMPORTBOUND =   (1<<10);
+  static const unsigned STATE_EXPORTBOUND =   (1<<11);
+  static const unsigned STATE_EXPORTING =     (1<<12);
+  static const unsigned STATE_IMPORTING =     (1<<13);
+  static const unsigned STATE_FRAGMENTING =   (1<<14);
+  static const unsigned STATE_STICKY =        (1<<15);  // sticky pin due to inode stickydirs
+  static const unsigned STATE_DNPINNEDFRAG =  (1<<16);  // dir is refragmenting
+  static const unsigned STATE_ASSIMRSTAT =    (1<<17);  // assimilating inode->frag rstats
+  static const unsigned STATE_DIRTYDFT =      (1<<18);  // dirty dirfragtree
+  static const unsigned STATE_BADFRAG =       (1<<19);  // bad dirfrag
+  static const unsigned STATE_AUXSUBTREE =    (1<<20);  // no subtree merge
+
+  // common states
+  static const unsigned STATE_CLEAN =  0;
+  static const unsigned STATE_INITIAL = 0;
+
+  // these state bits are preserved by an import/export
+  // ...except if the directory is hashed, in which case none of them are!
+  static const unsigned MASK_STATE_EXPORTED = 
+  (STATE_COMPLETE|STATE_DIRTY|STATE_DIRTYDFT|STATE_BADFRAG);
+  static const unsigned MASK_STATE_IMPORT_KEPT = 
+  (                                              
+   STATE_IMPORTING
+   |STATE_IMPORTBOUND|STATE_EXPORTBOUND
+   |STATE_FROZENTREE
+   |STATE_STICKY);
+  static const unsigned MASK_STATE_EXPORT_KEPT = 
+  (STATE_EXPORTING
+   |STATE_IMPORTBOUND|STATE_EXPORTBOUND
+   |STATE_FROZENTREE
+   |STATE_FROZENDIR
+   |STATE_STICKY);
+  static const unsigned MASK_STATE_FRAGMENT_KEPT = 
+  (STATE_DIRTY|
+   STATE_EXPORTBOUND |
+   STATE_IMPORTBOUND |
+   STATE_AUXSUBTREE |
+   STATE_REJOINUNDEF);
+
+  // -- rep spec --
+  static const int REP_NONE =     0;
+  static const int REP_ALL =      1;
+  static const int REP_LIST =     2;
+
+
+  static const unsigned EXPORT_NONCE  = 1;
+
+
+  // -- wait masks --
+  static const uint64_t WAIT_DENTRY       = (1<<0);  // wait for item to be in cache
+  static const uint64_t WAIT_COMPLETE     = (1<<1);  // wait for complete dir contents
+  static const uint64_t WAIT_FROZEN       = (1<<2);  // auth pins removed
+  static const uint64_t WAIT_CREATED     = (1<<3);  // new dirfrag is logged
+
+  static const int WAIT_DNLOCK_OFFSET = 4;
+
+  static const uint64_t WAIT_ANY_MASK = (uint64_t)(-1);
+  static const uint64_t WAIT_ATFREEZEROOT = (WAIT_UNFREEZE);
+  static const uint64_t WAIT_ATSUBTREEROOT = (WAIT_SINGLEAUTH);
+
+
+
+
+ public:
+  // context
+  MDCache  *cache;
+
+  CInode          *inode;  // my inode
+  frag_t           frag;   // my frag
+
+  bool is_lt(const MDSCacheObject *r) const override {
+    return dirfrag() < (static_cast<const CDir*>(r))->dirfrag();
+  }
+
+  fnode_t fnode;
+  snapid_t first;
+  compact_map<snapid_t,old_rstat_t> dirty_old_rstat;  // [value.first,key]
+
+  // my inodes with dirty rstat data
+  elist<CInode*> dirty_rstat_inodes;     
+
+  void resync_accounted_fragstat();
+  void resync_accounted_rstat();
+  void assimilate_dirty_rstat_inodes();
+  void assimilate_dirty_rstat_inodes_finish(MutationRef& mut, EMetaBlob *blob);
+
+protected:
+  version_t projected_version;
+  std::list<fnode_t*> projected_fnode;
+
+public:
+  elist<CDir*>::item item_dirty, item_new;
+
+
+public:
+  version_t get_version() const { return fnode.version; }
+  void set_version(version_t v) { 
+    assert(projected_fnode.empty());
+    projected_version = fnode.version = v; 
+  }
+  version_t get_projected_version() const { return projected_version; }
+
+  const fnode_t *get_projected_fnode() const {
+    if (projected_fnode.empty())
+      return &fnode;
+    else
+      return projected_fnode.back();
+  }
+
+  fnode_t *get_projected_fnode() {
+    if (projected_fnode.empty())
+      return &fnode;
+    else
+      return projected_fnode.back();
+  }
+  fnode_t *project_fnode();
+
+  void pop_and_dirty_projected_fnode(LogSegment *ls);
+  bool is_projected() const { return !projected_fnode.empty(); }
+  version_t pre_dirty(version_t min=0);
+  void _mark_dirty(LogSegment *ls);
+  void _set_dirty_flag() {
+    if (!state_test(STATE_DIRTY)) {
+      state_set(STATE_DIRTY);
+      get(PIN_DIRTY);
+    }
+  }
+  void mark_dirty(version_t pv, LogSegment *ls);
+  void mark_clean();
+
+  bool is_new() { return item_new.is_on_list(); }
+  void mark_new(LogSegment *ls);
+
+  bool is_bad() { return state_test(STATE_BADFRAG); }
+private:
+  void log_mark_dirty();
+
+public:
+  typedef std::map<dentry_key_t, CDentry*> map_t;
+
+  class scrub_info_t {
+  public:
+    /// inodes we contain with dirty scrub stamps
+    map<dentry_key_t,CInode*> dirty_scrub_stamps; // TODO: make use of this!
+    struct scrub_stamps {
+      version_t version;
+      utime_t time;
+      scrub_stamps() : version(0) {}
+      void operator=(const scrub_stamps &o) {
+        version = o.version;
+        time = o.time;
+      }
+    };
+
+    scrub_stamps recursive_start; // when we last started a recursive scrub
+    scrub_stamps last_recursive; // when we last finished a recursive scrub
+    scrub_stamps last_local; // when we last did a local scrub
+
+    bool directory_scrubbing; /// safety check
+    bool need_scrub_local;
+    bool last_scrub_dirty; /// is scrub info dirty or is it flushed to fnode?
+    bool pending_scrub_error;
+
+    /// these are lists of children in each stage of scrubbing
+    set<dentry_key_t> directories_to_scrub;
+    set<dentry_key_t> directories_scrubbing;
+    set<dentry_key_t> directories_scrubbed;
+    set<dentry_key_t> others_to_scrub;
+    set<dentry_key_t> others_scrubbing;
+    set<dentry_key_t> others_scrubbed;
+
+    ScrubHeaderRefConst header;
+
+    scrub_info_t() :
+      directory_scrubbing(false),
+      need_scrub_local(false),
+      last_scrub_dirty(false),
+      pending_scrub_error(false) {}
+  };
+  /**
+   * Call to start this CDir on a new scrub.
+   * @pre It is not currently scrubbing
+   * @pre The CDir is marked complete.
+   * @post It has set up its internal scrubbing state.
+   */
+  void scrub_initialize(const ScrubHeaderRefConst& header);
+  /**
+   * Get the next dentry to scrub. Gives you a CDentry* and its meaning. This
+   * function will give you all directory-representing dentries before any
+   * others.
+   * 0: success, you should scrub this CDentry right now
+   * EAGAIN: is currently fetching the next CDentry into memory for you.
+   *   It will activate your callback when done; try again when it does!
+   * ENOENT: there are no remaining dentries to scrub
+   * <0: There was an unexpected error
+   *
+   * @param cb An MDSInternalContext which will be activated only if
+   *   we return EAGAIN via rcode, or else ignored
+   * @param dnout CDentry * which you should next scrub, or NULL
+   * @returns a value as described above
+   */
+  int scrub_dentry_next(MDSInternalContext *cb, CDentry **dnout);
+  /**
+   * Get the currently scrubbing dentries. When returned, the passed-in
+   * list will be filled with all CDentry * which have been returned
+   * from scrub_dentry_next() but not sent back via scrub_dentry_finished().
+   */
+  void scrub_dentries_scrubbing(list<CDentry*> *out_dentries);
+  /**
+   * Report to the CDir that a CDentry has been scrubbed. Call this
+   * for every CDentry returned from scrub_dentry_next().
+   * @param dn The CDentry which has been scrubbed.
+   */
+  void scrub_dentry_finished(CDentry *dn);
+  /**
+   * Call this once all CDentries have been scrubbed, according to
+   * scrub_dentry_next's listing. It finalizes the scrub statistics.
+   */
+  void scrub_finished();
+  /**
+   * Tell the CDir to do a local scrub of itself.
+   * @pre The CDir is_complete().
+   * @returns true if the rstats and directory contents match, false otherwise.
+   */
+  bool scrub_local();
+private:
+  /**
+   * Create a scrub_info_t struct for the scrub_infop pointer.
+   */
+  void scrub_info_create() const;
+  /**
+   * Delete the scrub_infop if it's not got any useful data.
+   */
+  void scrub_maybe_delete_info();
+  /**
+   * Check the given set (presumably one of those in scrub_info_t) for the
+   * next key to scrub and look it up (or fail!).
+   */
+  int _next_dentry_on_set(set<dentry_key_t>& dns, bool missing_okay,
+                          MDSInternalContext *cb, CDentry **dnout);
+
+
+protected:
+  std::unique_ptr<scrub_info_t> scrub_infop;
+
+  // contents of this directory
+  map_t items;       // non-null AND null
+  unsigned num_head_items;
+  unsigned num_head_null;
+  unsigned num_snap_items;
+  unsigned num_snap_null;
+
+  int num_dirty;
+
+  // state
+  version_t committing_version;
+  version_t committed_version;
+
+  compact_set<string> stale_items;
+
+  // lock nesting, freeze
+  static int num_frozen_trees;
+  static int num_freezing_trees;
+
+  int dir_auth_pins;
+  int request_pins;
+
+  // cache control  (defined for authority; hints for replicas)
+  __s32      dir_rep;
+  compact_set<__s32> dir_rep_by;      // if dir_rep == REP_LIST
+
+  // popularity
+  dirfrag_load_vec_t pop_me;
+  dirfrag_load_vec_t pop_nested;
+  dirfrag_load_vec_t pop_auth_subtree;
+  dirfrag_load_vec_t pop_auth_subtree_nested;
+  utime_t last_popularity_sample;
+
+  load_spread_t pop_spread;
+
+  // and to provide density
+  int num_dentries_nested;
+  int num_dentries_auth_subtree;
+  int num_dentries_auth_subtree_nested;
+
+
+  // friends
+  friend class Migrator;
+  friend class CInode;
+  friend class MDCache;
+  friend class MDiscover;
+  friend class MDBalancer;
+
+  friend class CDirDiscover;
+  friend class CDirExport;
+  friend class C_IO_Dir_TMAP_Fetched;
+  friend class C_IO_Dir_OMAP_Fetched;
+  friend class C_IO_Dir_OMAP_FetchedMore;
+  friend class C_IO_Dir_Committed;
+
+  std::unique_ptr<bloom_filter> bloom;
+  /* If you set up the bloom filter, you must keep it accurate!
+   * It's deleted when you mark_complete() and is deliberately not serialized.*/
+
+ public:
+  CDir(CInode *in, frag_t fg, MDCache *mdcache, bool auth);
+
+  const scrub_info_t *scrub_info() const {
+    if (!scrub_infop) {
+      scrub_info_create();
+    }
+    return scrub_infop.get();
+  }
+
+
+  // -- accessors --
+  inodeno_t ino()     const { return inode->ino(); }          // deprecate me?
+  frag_t    get_frag()    const { return frag; }
+  dirfrag_t dirfrag() const { return dirfrag_t(inode->ino(), frag); }
+
+  CInode *get_inode()    { return inode; }
+  const CInode *get_inode() const { return inode; }
+  CDir *get_parent_dir() { return inode->get_parent_dir(); }
+
+  map_t::iterator begin() { return items.begin(); }
+  map_t::iterator end() { return items.end(); }
+  map_t::iterator lower_bound(dentry_key_t key) { return items.lower_bound(key); }
+
+  unsigned get_num_head_items() const { return num_head_items; }
+  unsigned get_num_head_null() const { return num_head_null; }
+  unsigned get_num_snap_items() const { return num_snap_items; }
+  unsigned get_num_snap_null() const { return num_snap_null; }
+  unsigned get_num_any() const { return num_head_items + num_head_null + num_snap_items + num_snap_null; }
+  
+  bool check_rstats(bool scrub=false);
+
+  void inc_num_dirty() { num_dirty++; }
+  void dec_num_dirty() { 
+    assert(num_dirty > 0);
+    num_dirty--; 
+  }
+  int get_num_dirty() const {
+    return num_dirty;
+  }
+
+  int64_t get_frag_size() const {
+    return get_projected_fnode()->fragstat.size();
+  }
+
+  // -- dentries and inodes --
+ public:
+  CDentry* lookup_exact_snap(const std::string& dname, snapid_t last);
+  CDentry* lookup(const std::string& n, snapid_t snap=CEPH_NOSNAP);
+  CDentry* lookup(const char *n, snapid_t snap=CEPH_NOSNAP) {
+    return lookup(std::string(n), snap);
+  }
+
+  CDentry* add_null_dentry(const std::string& dname, 
+                          snapid_t first=2, snapid_t last=CEPH_NOSNAP);
+  CDentry* add_primary_dentry(const std::string& dname, CInode *in, 
+                             snapid_t first=2, snapid_t last=CEPH_NOSNAP);
+  CDentry* add_remote_dentry(const std::string& dname, inodeno_t ino, unsigned char d_type, 
+                            snapid_t first=2, snapid_t last=CEPH_NOSNAP);
+  void remove_dentry( CDentry *dn );         // delete dentry
+  void link_remote_inode( CDentry *dn, inodeno_t ino, unsigned char d_type);
+  void link_remote_inode( CDentry *dn, CInode *in );
+  void link_primary_inode( CDentry *dn, CInode *in );
+  void unlink_inode(CDentry *dn, bool adjust_lru=true);
+  void try_remove_unlinked_dn(CDentry *dn);
+
+  void add_to_bloom(CDentry *dn);
+  bool is_in_bloom(const std::string& name);
+  bool has_bloom() { return (bloom ? true : false); }
+  void remove_bloom() {
+    bloom.reset();
+  }
+private:
+  void link_inode_work( CDentry *dn, CInode *in );
+  void unlink_inode_work( CDentry *dn );
+  void remove_null_dentries();
+  void purge_stale_snap_data(const std::set<snapid_t>& snaps);
+public:
+  void try_remove_dentries_for_stray();
+  bool try_trim_snap_dentry(CDentry *dn, const std::set<snapid_t>& snaps);
+
+
+public:
+  void split(int bits, list<CDir*>& subs, list<MDSInternalContextBase*>& waiters, bool replay);
+  void merge(list<CDir*>& subs, list<MDSInternalContextBase*>& waiters, bool replay);
+
+  bool should_split() const {
+    return (int)get_frag_size() > g_conf->mds_bal_split_size;
+  }
+  bool should_split_fast() const;
+  bool should_merge() const {
+    return (int)get_frag_size() < g_conf->mds_bal_merge_size;
+  }
+
+private:
+  void prepare_new_fragment(bool replay);
+  void prepare_old_fragment(map<string_snap_t, std::list<MDSInternalContextBase*> >& dentry_waiters, bool replay);
+  void steal_dentry(CDentry *dn);  // from another dir.  used by merge/split.
+  void finish_old_fragment(list<MDSInternalContextBase*>& waiters, bool replay);
+  void init_fragment_pins();
+
+
+  // -- authority --
+  /*
+   *     normal: <parent,unknown>   !subtree_root
+   * delegation: <mds,unknown>       subtree_root
+   *  ambiguous: <mds1,mds2>         subtree_root
+   *             <parent,mds2>       subtree_root     
+   */
+  mds_authority_t dir_auth;
+
+  std::string get_path() const;
+
+ public:
+  mds_authority_t authority() const override;
+  mds_authority_t get_dir_auth() const { return dir_auth; }
+  void set_dir_auth(mds_authority_t a);
+  void set_dir_auth(mds_rank_t a) { set_dir_auth(mds_authority_t(a, CDIR_AUTH_UNKNOWN)); }
+  bool is_ambiguous_dir_auth() const {
+    return dir_auth.second != CDIR_AUTH_UNKNOWN;
+  }
+  bool is_full_dir_auth() const {
+    return is_auth() && !is_ambiguous_dir_auth();
+  }
+  bool is_full_dir_nonauth() const {
+    return !is_auth() && !is_ambiguous_dir_auth();
+  }
+  
+  bool is_subtree_root() const {
+    return dir_auth != CDIR_AUTH_DEFAULT;
+  }
+
+  bool contains(CDir *x);  // true if we are x or an ancestor of x 
+
+
+  // for giving to clients
+  void get_dist_spec(std::set<mds_rank_t>& ls, mds_rank_t auth) {
+    if (is_rep()) {
+      list_replicas(ls);
+      if (!ls.empty()) 
+       ls.insert(auth);
+    }
+  }
+  void encode_dirstat(bufferlist& bl, mds_rank_t whoami) {
+    /*
+     * note: encoding matches struct ceph_client_reply_dirfrag
+     */
+    frag_t frag = get_frag();
+    mds_rank_t auth;
+    std::set<mds_rank_t> dist;
+    
+    auth = dir_auth.first;
+    if (is_auth()) 
+      get_dist_spec(dist, whoami);
+
+    ::encode(frag, bl);
+    ::encode(auth, bl);
+    ::encode(dist, bl);
+  }
+
+  void _encode_base(bufferlist& bl) {
+    ::encode(first, bl);
+    ::encode(fnode, bl);
+    ::encode(dir_rep, bl);
+    ::encode(dir_rep_by, bl);
+  }
+  void _decode_base(bufferlist::iterator& p) {
+    ::decode(first, p);
+    ::decode(fnode, p);
+    ::decode(dir_rep, p);
+    ::decode(dir_rep_by, p);
+  }
+  void encode_replica(mds_rank_t who, bufferlist& bl) {
+    __u32 nonce = add_replica(who);
+    ::encode(nonce, bl);
+    _encode_base(bl);
+  }
+  void decode_replica(bufferlist::iterator& p) {
+    __u32 nonce;
+    ::decode(nonce, p);
+    replica_nonce = nonce;
+    _decode_base(p);
+  }
+
+
+
+  // -- state --
+  bool is_complete() { return state & STATE_COMPLETE; }
+  bool is_exporting() { return state & STATE_EXPORTING; }
+  bool is_importing() { return state & STATE_IMPORTING; }
+  bool is_dirty_dft() { return state & STATE_DIRTYDFT; }
+
+  int get_dir_rep() const { return dir_rep; }
+  bool is_rep() const { 
+    if (dir_rep == REP_NONE) return false;
+    return true;
+  }
+  // -- fetch --
+  object_t get_ondisk_object() { 
+    return file_object_t(ino(), frag);
+  }
+  void fetch(MDSInternalContextBase *c, bool ignore_authpinnability=false);
+  void fetch(MDSInternalContextBase *c, const std::string& want_dn, bool ignore_authpinnability=false);
+  void fetch(MDSInternalContextBase *c, const std::set<dentry_key_t>& keys);
+protected:
+  compact_set<string> wanted_items;
+
+  void _omap_fetch(MDSInternalContextBase *fin, const std::set<dentry_key_t>& keys);
+  void _omap_fetch_more(
+    bufferlist& hdrbl, std::map<std::string, bufferlist>& omap,
+    MDSInternalContextBase *fin);
+  CDentry *_load_dentry(
+      const std::string &key,
+      const std::string &dname,
+      snapid_t last,
+      bufferlist &bl,
+      int pos,
+      const std::set<snapid_t> *snaps,
+      bool *force_dirty,
+      list<CInode*> *undef_inodes);
+
+  /**
+   * Mark this fragment as BADFRAG (common part of go_bad and go_bad_dentry)
+   */
+  void _go_bad();
+
+  /**
+   * Go bad due to a damaged dentry (register with damagetable and go BADFRAG)
+   */
+  void go_bad_dentry(snapid_t last, const std::string &dname);
+
+  /**
+   * Go bad due to a damaged header (register with damagetable and go BADFRAG)
+   */
+  void go_bad(bool complete);
+
+  void _omap_fetched(bufferlist& hdrbl, std::map<std::string, bufferlist>& omap,
+                    bool complete, int r);
+
+  // -- commit --
+  compact_map<version_t, std::list<MDSInternalContextBase*> > waiting_for_commit;
+  void _commit(version_t want, int op_prio);
+  void _omap_commit(int op_prio);
+  void _encode_dentry(CDentry *dn, bufferlist& bl, const std::set<snapid_t> *snaps);
+  void _committed(int r, version_t v);
+public:
+#if 0  // unused?
+  void wait_for_commit(Context *c, version_t v=0);
+#endif
+  void commit_to(version_t want);
+  void commit(version_t want, MDSInternalContextBase *c,
+             bool ignore_authpinnability=false, int op_prio=-1);
+
+  // -- dirtyness --
+  version_t get_committing_version() const { return committing_version; }
+  version_t get_committed_version() const { return committed_version; }
+  void set_committed_version(version_t v) { committed_version = v; }
+
+  void mark_complete();
+
+
+  // -- reference counting --
+  void first_get() override;
+  void last_put() override;
+
+  void request_pin_get() {
+    if (request_pins == 0) get(PIN_REQUEST);
+    request_pins++;
+  }
+  void request_pin_put() {
+    request_pins--;
+    if (request_pins == 0) put(PIN_REQUEST);
+  }
+
+  // -- waiters --
+protected:
+  compact_map< string_snap_t, std::list<MDSInternalContextBase*> > waiting_on_dentry;
+
+public:
+  bool is_waiting_for_dentry(const std::string& dname, snapid_t snap) {
+    return waiting_on_dentry.count(string_snap_t(dname, snap));
+  }
+  void add_dentry_waiter(const std::string& dentry, snapid_t snap, MDSInternalContextBase *c);
+  void take_dentry_waiting(const std::string& dentry, snapid_t first, snapid_t last, std::list<MDSInternalContextBase*>& ls);
+  void take_sub_waiting(std::list<MDSInternalContextBase*>& ls);  // dentry or ino
+
+  void add_waiter(uint64_t mask, MDSInternalContextBase *c) override;
+  void take_waiting(uint64_t mask, std::list<MDSInternalContextBase*>& ls) override;  // may include dentry waiters
+  void finish_waiting(uint64_t mask, int result = 0);    // ditto
+  
+
+  // -- import/export --
+  void encode_export(bufferlist& bl);
+  void finish_export(utime_t now);
+  void abort_export() {
+    put(PIN_TEMPEXPORTING);
+  }
+  void decode_import(bufferlist::iterator& blp, utime_t now, LogSegment *ls);
+
+  // -- auth pins --
+  bool can_auth_pin() const override { return is_auth() && !(is_frozen() || is_freezing()); }
+  int get_cum_auth_pins() const { return auth_pins + nested_auth_pins; }
+  int get_auth_pins() const { return auth_pins; }
+  int get_nested_auth_pins() const { return nested_auth_pins; }
+  int get_dir_auth_pins() const { return dir_auth_pins; }
+  void auth_pin(void *who) override;
+  void auth_unpin(void *who) override;
+
+  void adjust_nested_auth_pins(int inc, int dirinc, void *by);
+  void verify_fragstat();
+
+  // -- freezing --
+  bool freeze_tree();
+  void _freeze_tree();
+  void unfreeze_tree();
+
+  bool freeze_dir();
+  void _freeze_dir();
+  void unfreeze_dir();
+
+  void maybe_finish_freeze();
+
+  bool is_freezing() const override { return is_freezing_tree() || is_freezing_dir(); }
+  bool is_freezing_tree() const;
+  bool is_freezing_tree_root() const { return state & STATE_FREEZINGTREE; }
+  bool is_freezing_dir() const { return state & STATE_FREEZINGDIR; }
+
+  bool is_frozen() const override { return is_frozen_dir() || is_frozen_tree(); }
+  bool is_frozen_tree() const;
+  bool is_frozen_tree_root() const { return state & STATE_FROZENTREE; }
+  bool is_frozen_dir() const { return state & STATE_FROZENDIR; }
+  
+  bool is_freezeable(bool freezing=false) const {
+    // no nested auth pins.
+    if ((auth_pins-freezing) > 0 || nested_auth_pins > 0) 
+      return false;
+
+    // inode must not be frozen.
+    if (!is_subtree_root() && inode->is_frozen())
+      return false;
+
+    return true;
+  }
+  bool is_freezeable_dir(bool freezing=false) const {
+    if ((auth_pins-freezing) > 0 || dir_auth_pins > 0) 
+      return false;
+
+    // if not subtree root, inode must not be frozen (tree--frozen_dir is okay).
+    if (!is_subtree_root() && inode->is_frozen() && !inode->is_frozen_dir())
+      return false;
+
+    return true;
+  }
+
+  CDir *get_frozen_tree_root();
+
+
+  ostream& print_db_line_prefix(ostream& out) override;
+  void print(ostream& out) override;
+  void dump(Formatter *f) const;
+};
+
+#endif