]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/client/Inode.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / client / Inode.cc
index 2fadb2b5bab1823a9e83ef8c6e056203ab5daefb..5c1523f2f427e52410f9de038f4c777a3570d709 100644 (file)
 
 #include "mds/flock.h"
 
+using std::dec;
+using std::list;
+using std::oct;
+using std::ostream;
+using std::string;
+
 Inode::~Inode()
 {
-  cap_item.remove_myself();
+  delay_cap_item.remove_myself();
+  dirty_cap_item.remove_myself(); 
   snaprealm_item.remove_myself();
 
   if (snapdir_parent) {
@@ -25,37 +32,39 @@ Inode::~Inode()
   if (!oset.objects.empty()) {
     lsubdout(client->cct, client, 0) << __func__ << ": leftover objects on inode 0x"
       << std::hex << ino << std::dec << dendl;
-    assert(oset.objects.empty());
+    ceph_assert(oset.objects.empty());
   }
 
   if (!delegations.empty()) {
     lsubdout(client->cct, client, 0) << __func__ << ": leftover delegations on inode 0x"
       << std::hex << ino << std::dec << dendl;
-    assert(delegations.empty());
+    ceph_assert(delegations.empty());
   }
-
-  delete fcntl_locks;
-  delete flock_locks;
 }
 
 ostream& operator<<(ostream &out, const Inode &in)
 {
   out << in.vino() << "("
       << "faked_ino=" << in.faked_ino
-      << " ref=" << in._ref
+      << " nref=" << in.get_nref()
       << " ll_ref=" << in.ll_ref
       << " cap_refs=" << in.cap_refs
       << " open=" << in.open_by_mode
       << " mode=" << oct << in.mode << dec
       << " size=" << in.size << "/" << in.max_size
+      << " nlink=" << in.nlink
+      << " btime=" << in.btime
       << " mtime=" << in.mtime
+      << " ctime=" << in.ctime
       << " caps=" << ccap_string(in.caps_issued());
   if (!in.caps.empty()) {
     out << "(";
-    for (auto p = in.caps.begin(); p != in.caps.end(); ++p) {
-      if (p != in.caps.begin())
+    bool first = true;
+    for (const auto &pair : in.caps) {
+      if (!first)
         out << ',';
-      out << p->first << '=' << ccap_string(p->second->issued);
+      out << pair.first << '=' << ccap_string(pair.second.issued);
+      first = false;
     }
     out << ")";
   }
@@ -70,8 +79,8 @@ ostream& operator<<(ostream &out, const Inode &in)
   if (in.is_file())
     out << " " << in.oset;
 
-  if (!in.dn_set.empty())
-    out << " parents=" << in.dn_set;
+  if (!in.dentries.empty())
+    out << " parents=" << in.dentries;
 
   if (in.is_dir() && in.has_dir_layout())
     out << " has_dir_layout";
@@ -86,14 +95,25 @@ ostream& operator<<(ostream &out, const Inode &in)
 
 void Inode::make_long_path(filepath& p)
 {
-  if (!dn_set.empty()) {
-    assert((*dn_set.begin())->dir && (*dn_set.begin())->dir->parent_inode);
-    (*dn_set.begin())->dir->parent_inode->make_long_path(p);
-    p.push_dentry((*dn_set.begin())->name);
+  if (!dentries.empty()) {
+    Dentry *dn = get_first_parent();
+    ceph_assert(dn->dir && dn->dir->parent_inode);
+    dn->dir->parent_inode->make_long_path(p);
+    p.push_dentry(dn->name);
   } else if (snapdir_parent) {
-    snapdir_parent->make_nosnap_relative_path(p);
-    string empty;
-    p.push_dentry(empty);
+    make_nosnap_relative_path(p);
+  } else
+    p = filepath(ino);
+}
+
+void Inode::make_short_path(filepath& p)
+{
+  if (!dentries.empty()) {
+    Dentry *dn = get_first_parent();
+    ceph_assert(dn->dir && dn->dir->parent_inode);
+    p = filepath(dn->name, dn->dir->parent_inode->ino);
+  } else if (snapdir_parent) {
+    make_nosnap_relative_path(p);
   } else
     p = filepath(ino);
 }
@@ -111,10 +131,11 @@ void Inode::make_nosnap_relative_path(filepath& p)
     snapdir_parent->make_nosnap_relative_path(p);
     string empty;
     p.push_dentry(empty);
-  } else if (!dn_set.empty()) {
-    assert((*dn_set.begin())->dir && (*dn_set.begin())->dir->parent_inode);
-    (*dn_set.begin())->dir->parent_inode->make_nosnap_relative_path(p);
-    p.push_dentry((*dn_set.begin())->name);
+  } else if (!dentries.empty()) {
+    Dentry *dn = get_first_parent();
+    ceph_assert(dn->dir && dn->dir->parent_inode);
+    dn->dir->parent_inode->make_nosnap_relative_path(p);
+    p.push_dentry(dn->name);
   } else {
     p = filepath(ino);
   }
@@ -122,6 +143,10 @@ void Inode::make_nosnap_relative_path(filepath& p)
 
 void Inode::get_open_ref(int mode)
 {
+  client->inc_opened_files();
+  if (open_by_mode[mode] == 0) {
+    client->inc_opened_inodes();
+  }
   open_by_mode[mode]++;
   break_deleg(!(mode & CEPH_FILE_MODE_WR));
 }
@@ -129,8 +154,13 @@ void Inode::get_open_ref(int mode)
 bool Inode::put_open_ref(int mode)
 {
   //cout << "open_by_mode[" << mode << "] " << open_by_mode[mode] << " -> " << (open_by_mode[mode]-1) << std::endl;
-  if (--open_by_mode[mode] == 0)
+  auto& ref = open_by_mode.at(mode);
+  ceph_assert(ref > 0);
+  client->dec_opened_files();
+  if (--ref == 0) {
+    client->dec_opened_inodes();
     return true;
+  }
   return false;
 }
 
@@ -157,7 +187,7 @@ int Inode::put_cap_ref(int cap)
       int c = 1 << n;
       if (cap_refs[c] <= 0) {
        lderr(client->cct) << "put_cap_ref " << ccap_string(c) << " went negative on " << *this << dendl;
-       assert(cap_refs[c] > 0);
+       ceph_assert(cap_refs[c] > 0);
       }
       if (--cap_refs[c] == 0)
         last |= c;
@@ -174,14 +204,14 @@ bool Inode::is_any_caps()
   return !caps.empty() || snap_caps;
 }
 
-bool Inode::cap_is_valid(Cap* cap) const
+bool Inode::cap_is_valid(const Cap &cap) const
 {
   /*cout << "cap_gen     " << cap->session-> cap_gen << std::endl
     << "session gen " << cap->gen << std::endl
     << "cap expire  " << cap->session->cap_ttl << std::endl
     << "cur time    " << ceph_clock_now(cct) << std::endl;*/
-  if ((cap->session->cap_gen <= cap->gen)
-      && (ceph_clock_now() < cap->session->cap_ttl)) {
+  if ((cap.session->cap_gen <= cap.gen)
+      && (ceph_clock_now() < cap.session->cap_ttl)) {
     return true;
   }
   return false;
@@ -191,14 +221,12 @@ int Inode::caps_issued(int *implemented) const
 {
   int c = snap_caps;
   int i = 0;
-  for (map<mds_rank_t,Cap*>::const_iterator it = caps.begin();
-       it != caps.end();
-       ++it)
-    if (cap_is_valid(it->second)) {
-      c |= it->second->issued;
-      i |= it->second->implemented;
+  for (const auto &[mds, cap] : caps) {
+    if (cap_is_valid(cap)) {
+      c |= cap.issued;
+      i |= cap.implemented;
     }
-
+  }
   // exclude caps issued by non-auth MDS, but are been revoking by
   // the auth MDS. The non-auth MDS should be revoking/exporting
   // these caps, but the message is delayed.
@@ -210,16 +238,12 @@ int Inode::caps_issued(int *implemented) const
   return c;
 }
 
-void Inode::touch_cap(Cap *cap)
-{
-  // move to back of LRU
-  cap->session->caps.push_back(&cap->cap_item);
-}
-
 void Inode::try_touch_cap(mds_rank_t mds)
 {
-  if (caps.count(mds))
-    touch_cap(caps[mds]);
+  auto it = caps.find(mds);
+  if (it != caps.end()) {
+    it->second.touch();
+  }
 }
 
 /**
@@ -230,6 +254,7 @@ void Inode::try_touch_cap(mds_rank_t mds)
  * This is the bog standard "check whether we have the required caps" operation.
  * Typically, we only check against the capset that is currently "issued".
  * In other words, we ignore caps that have been revoked but not yet released.
+ * Also account capability hit/miss stats.
  *
  * Some callers (particularly those doing attribute retrieval) can also make
  * use of the full set of "implemented" caps to satisfy requests from the
@@ -247,22 +272,23 @@ bool Inode::caps_issued_mask(unsigned mask, bool allow_impl)
     return true;
   // prefer auth cap
   if (auth_cap &&
-      cap_is_valid(auth_cap) &&
+      cap_is_valid(*auth_cap) &&
       (auth_cap->issued & mask) == mask) {
-    touch_cap(auth_cap);
+    auth_cap->touch();
+    client->cap_hit();
     return true;
   }
   // try any cap
-  for (map<mds_rank_t,Cap*>::iterator it = caps.begin();
-       it != caps.end();
-       ++it) {
-    if (cap_is_valid(it->second)) {
-      if ((it->second->issued & mask) == mask) {
-       touch_cap(it->second);
+  for (auto &pair : caps) {
+    Cap &cap = pair.second;
+    if (cap_is_valid(cap)) {
+      if ((cap.issued & mask) == mask) {
+        cap.touch();
+       client->cap_hit();
        return true;
       }
-      c |= it->second->issued;
-      i |= it->second->implemented;
+      c |= cap.issued;
+      i |= cap.implemented;
     }
   }
 
@@ -271,34 +297,32 @@ bool Inode::caps_issued_mask(unsigned mask, bool allow_impl)
 
   if ((c & mask) == mask) {
     // bah.. touch them all
-    for (map<mds_rank_t,Cap*>::iterator it = caps.begin();
-        it != caps.end();
-        ++it)
-      touch_cap(it->second);
+    for (auto &pair : caps) {
+      pair.second.touch();
+    }
+    client->cap_hit();
     return true;
   }
+
+  client->cap_miss();
   return false;
 }
 
 int Inode::caps_used()
 {
   int w = 0;
-  for (map<int,int>::iterator p = cap_refs.begin();
-       p != cap_refs.end();
-       ++p)
-    if (p->second)
-      w |= p->first;
+  for (const auto &[cap, cnt] : cap_refs)
+    if (cnt)
+      w |= cap;
   return w;
 }
 
 int Inode::caps_file_wanted()
 {
   int want = 0;
-  for (map<int,int>::iterator p = open_by_mode.begin();
-       p != open_by_mode.end();
-       ++p)
-    if (p->second)
-      want |= ceph_caps_for_mode(p->first);
+  for (const auto &[mode, cnt] : open_by_mode)
+    if (cnt)
+      want |= ceph_caps_for_mode(mode);
   return want;
 }
 
@@ -313,8 +337,9 @@ int Inode::caps_wanted()
 int Inode::caps_mds_wanted()
 {
   int want = 0;
-  for (auto it = caps.begin(); it != caps.end(); ++it)
-    want |= it->second->wanted;
+  for (const auto &pair : caps) {
+    want |= pair.second.wanted;
+  }
   return want;
 }
 
@@ -326,8 +351,8 @@ int Inode::caps_dirty()
 const UserPerm* Inode::get_best_perms()
 {
   const UserPerm *perms = NULL;
-  for (const auto ci : caps) {
-    const UserPerm& iperm = ci.second->latest_perms;
+  for (const auto &pair : caps) {
+    const UserPerm& iperm = pair.second.latest_perms;
     if (!perms) { // we don't have any, take what's present
       perms = &iperm;
     } else if (iperm.uid() == uid) {
@@ -358,10 +383,10 @@ Dir *Inode::open_dir()
   if (!dir) {
     dir = new Dir(this);
     lsubdout(client->cct, client, 15) << "open_dir " << dir << " on " << this << dendl;
-    assert(dn_set.size() < 2); // dirs can't be hard-linked
-    if (!dn_set.empty())
-      (*dn_set.begin())->get();      // pin dentry
-    get();                  // pin inode
+    ceph_assert(dentries.size() < 2); // dirs can't be hard-linked
+    if (!dentries.empty())
+      get_first_parent()->get();      // pin dentry
+    iget();                  // pin inode
   }
   return dir;
 }
@@ -379,22 +404,6 @@ bool Inode::check_mode(const UserPerm& perms, unsigned want)
   return (mode & want) == want;
 }
 
-void Inode::get() {
-  _ref++;
-  lsubdout(client->cct, client, 15) << "inode.get on " << this << " " <<  ino << '.' << snapid
-                                   << " now " << _ref << dendl;
-}
-
-//private method to put a reference; see Client::put_inode()
-int Inode::_put(int n) {
-  _ref -= n;
-  lsubdout(client->cct, client, 15) << "inode.put on " << this << " " << ino << '.' << snapid
-                                   << " now " << _ref << dendl;
-  assert(_ref >= 0);
-  return _ref;
-}
-
-
 void Inode::dump(Formatter *f) const
 {
   f->dump_stream("ino") << ino;
@@ -442,23 +451,34 @@ void Inode::dump(Formatter *f) const
   f->dump_unsigned("flags", flags);
 
   if (is_dir()) {
-    if (!dir_contacts.empty()) {
-      f->open_object_section("dir_contants");
-      for (set<int>::iterator p = dir_contacts.begin(); p != dir_contacts.end(); ++p)
-       f->dump_int("mds", *p);
-      f->close_section();
-    }
     f->dump_int("dir_hashed", (int)dir_hashed);
     f->dump_int("dir_replicated", (int)dir_replicated);
+    if (dir_replicated) {
+      f->open_array_section("dirfrags");
+      for (const auto &frag : frag_repmap) {
+        f->open_object_section("frags");
+        CachedStackStringStream css;
+        *css << std::hex << frag.first.value() << "/" << std::dec << frag.first.bits();
+        f->dump_string("frag", css->strv());
+
+        f->open_array_section("repmap");
+        for (const auto &mds : frag.second) {
+          f->dump_int("mds", mds);
+        }
+        f->close_section();
+
+        f->close_section();
+      }
+      f->close_section();
+    }
   }
 
   f->open_array_section("caps");
-  for (map<mds_rank_t,Cap*>::const_iterator p = caps.begin(); p != caps.end(); ++p) {
+  for (const auto &pair : caps) {
     f->open_object_section("cap");
-    f->dump_int("mds", p->first);
-    if (p->second == auth_cap)
+    if (&pair.second == auth_cap)
       f->dump_int("auth", 1);
-    p->second->dump(f);
+    pair.second.dump(f);
     f->close_section();
   }
   f->close_section();
@@ -528,15 +548,15 @@ void Inode::dump(Formatter *f) const
   if (requested_max_size != max_size)
     f->dump_unsigned("requested_max_size", requested_max_size);
 
-  f->dump_int("ref", _ref);
+  f->dump_int("nref", get_nref());
   f->dump_int("ll_ref", ll_ref);
 
-  if (!dn_set.empty()) {
+  if (!dentries.empty()) {
     f->open_array_section("parents");
-    for (set<Dentry*>::const_iterator p = dn_set.begin(); p != dn_set.end(); ++p) {
+    for (const auto &&dn : dentries) {
       f->open_object_section("dentry");
-      f->dump_stream("dir_ino") << (*p)->dir->parent_inode->ino;
-      f->dump_string("name", (*p)->name);
+      f->dump_stream("dir_ino") << dn->dir->parent_inode->ino;
+      f->dump_string("name", dn->name);
       f->close_section();
     }
     f->close_section();
@@ -546,7 +566,7 @@ void Inode::dump(Formatter *f) const
 void Cap::dump(Formatter *f) const
 {
   f->dump_int("mds", session->mds_num);
-  f->dump_stream("ino") << inode->ino;
+  f->dump_stream("ino") << inode.ino;
   f->dump_unsigned("cap_id", cap_id);
   f->dump_stream("issued") << ccap_string(issued);
   if (implemented != issued)
@@ -667,13 +687,13 @@ int Inode::set_deleg(Fh *fh, unsigned type, ceph_deleg_cb_t cb, void *priv)
    * allow it, with an unusual error to make it clear.
    */
   if (!client->get_deleg_timeout())
-    return -ETIME;
+    return -CEPHFS_ETIME;
 
   // Just say no if we have any recalled delegs still outstanding
   if (has_recalled_deleg()) {
     lsubdout(client->cct, client, 10) << __func__ <<
          ": has_recalled_deleg" << dendl;
-    return -EAGAIN;
+    return -CEPHFS_EAGAIN;
   }
 
   // check vs. currently open files on this inode
@@ -682,17 +702,17 @@ int Inode::set_deleg(Fh *fh, unsigned type, ceph_deleg_cb_t cb, void *priv)
     if (open_count_for_write()) {
       lsubdout(client->cct, client, 10) << __func__ <<
            ": open for write" << dendl;
-      return -EAGAIN;
+      return -CEPHFS_EAGAIN;
     }
     break;
   case CEPH_DELEGATION_WR:
     if (open_count() > 1) {
       lsubdout(client->cct, client, 10) << __func__ << ": open" << dendl;
-      return -EAGAIN;
+      return -CEPHFS_EAGAIN;
     }
     break;
   default:
-    return -EINVAL;
+    return -CEPHFS_EINVAL;
   }
 
   /*
@@ -713,7 +733,7 @@ int Inode::set_deleg(Fh *fh, unsigned type, ceph_deleg_cb_t cb, void *priv)
   if (!caps_issued_mask(need)) {
     lsubdout(client->cct, client, 10) << __func__ << ": cap mismatch, have="
       << ccap_string(caps_issued()) << " need=" << ccap_string(need) << dendl;
-    return -EAGAIN;
+    return -CEPHFS_EAGAIN;
   }
 
   for (list<Delegation>::iterator d = delegations.begin();
@@ -747,3 +767,47 @@ void Inode::unset_deleg(Fh *fh)
     }
   }
 }
+
+/**
+* mark_caps_dirty - mark some caps dirty
+* @caps: the dirty caps
+*
+* note that if there is no dirty and flushing caps before, we need to pin this inode.
+* it will be unpined by handle_cap_flush_ack when there are no dirty and flushing caps.
+*/
+void Inode::mark_caps_dirty(int caps)
+{
+  /*
+   * If auth_cap is nullptr means the reonnecting is not finished or
+   * already rejected.
+   */
+  if (!auth_cap) {
+    ceph_assert(!dirty_caps);
+
+    lsubdout(client->cct, client, 1) << __func__ << " " << *this << " dirty caps '" << ccap_string(caps)
+            << "', but no auth cap." << dendl;
+    return;
+  }
+
+  lsubdout(client->cct, client, 10) << __func__ << " " << *this << " " << ccap_string(dirty_caps) << " -> "
+           << ccap_string(dirty_caps | caps) << dendl;
+
+  if (caps && !caps_dirty())
+    iget();
+
+  dirty_caps |= caps;
+  auth_cap->session->get_dirty_list().push_back(&dirty_cap_item);
+  client->cap_delay_requeue(this);
+}
+
+/**
+* mark_caps_clean - only clean the dirty_caps and caller should start flushing the dirty caps.
+*/
+void Inode::mark_caps_clean()
+{
+  lsubdout(client->cct, client, 10) << __func__ << " " << *this << dendl;
+  dirty_caps = 0;
+  dirty_cap_item.remove_myself();
+}
+
+