]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mds: maintain per-client write ranges instead of single max_size
authorSage Weil <sage@newdream.net>
Fri, 10 Jul 2009 21:59:12 +0000 (14:59 -0700)
committerSage Weil <sage@newdream.net>
Fri, 10 Jul 2009 22:32:56 +0000 (15:32 -0700)
We need to track individual clients' ability to write to a file in
order to behave rationally when a client dies or fails to reconnect
after an mds restart.

There is also a behavior change hiding in here: before we were
adjusting max_size based on _wanted_ caps, but that was unsafe,
since a client may have the old max_size value and initiate a write
after informing the mds it no longer wants said caps.  There will
undoubtably be some fallout from that change...

Also clean up instances of memset() on inode_t and friends.  Define
and use constructors instead.

src/include/ceph_fs.h
src/mds/CInode.cc
src/mds/CInode.h
src/mds/Locker.cc
src/mds/MDCache.cc
src/mds/Server.cc
src/mds/mdstypes.h
src/messages/MClientReply.h

index 5469eaf6f2cb7760398ffad5920557c848ef8e66..8225c5017601c02d883fc8e98abb77f5c72249c2 100644 (file)
@@ -27,7 +27,7 @@
 #define CEPH_MDS_PROTOCOL     9 /* cluster internal */
 #define CEPH_MON_PROTOCOL     4 /* cluster internal */
 #define CEPH_OSDC_PROTOCOL   19 /* public/client */
-#define CEPH_MDSC_PROTOCOL   25 /* public/client */
+#define CEPH_MDSC_PROTOCOL   26 /* public/client */
 #define CEPH_MONC_PROTOCOL   14 /* public/client */
 
 
index d9b5fdf76fcdf4cb50e60f37bd780a292b96985a..f183394a89521fd78909e4186b8b478ce9728e2a 100644 (file)
@@ -114,8 +114,6 @@ ostream& operator<<(ostream& out, CInode& in)
     //if (in.inode.dirstat.version > 10000) out << " BADDIRSTAT";
   } else {
     out << " s=" << in.inode.size;
-    if (in.inode.max_size)
-      out << "/" << in.inode.max_size;
     out << " nl=" << in.inode.nlink;
   }
 
@@ -140,6 +138,10 @@ ostream& operator<<(ostream& out, CInode& in)
 
   // hack: spit out crap on which clients have caps
   if (!in.get_client_caps().empty()) {
+
+    if (in.inode.client_ranges.size())
+      out << " cr=" << in.inode.client_ranges;
+
     out << " caps={";
     for (map<int,Capability*>::iterator it = in.get_client_caps().begin();
          it != in.get_client_caps().end();
@@ -770,10 +772,10 @@ void CInode::encode_lock_state(int type, bufferlist& bl)
     if (is_auth()) {
       ::encode(inode.layout, bl);
       ::encode(inode.size, bl);
-      ::encode(inode.max_size, bl);
       ::encode(inode.mtime, bl);
       ::encode(inode.atime, bl);
       ::encode(inode.time_warp_seq, bl);
+      ::encode(inode.client_ranges, bl);
     }
 
     {
@@ -915,10 +917,10 @@ void CInode::decode_lock_state(int type, bufferlist& bl)
     if (!is_auth()) {
       ::decode(inode.layout, p);
       ::decode(inode.size, p);
-      ::decode(inode.max_size, p);
       ::decode(inode.mtime, p);
       ::decode(inode.atime, p);
       ::decode(inode.time_warp_seq, p);
+      ::decode(inode.client_ranges, p);
     }
 
     {
@@ -1537,7 +1539,10 @@ bool CInode::encode_inodestat(bufferlist& bl, Session *session,
   i = pfile ? pi:oi;
   e.layout = i->layout;
   e.size = i->size;
-  e.max_size = i->max_size;
+  if (i->client_ranges.count(client))
+    e.max_size = i->client_ranges[client].last;
+  else
+    e.max_size = 0;
   e.truncate_seq = i->truncate_seq;
   e.truncate_size = i->truncate_size;
   i->mtime.encode_timeval(&e.mtime);
@@ -1673,7 +1678,10 @@ void CInode::encode_cap_message(MClientCaps *m, Capability *cap)
   i = pfile ? pi:oi;
   m->head.layout = i->layout;
   m->head.size = i->size;
-  m->head.max_size = i->max_size;
+  if (i->client_ranges.count(client))
+    m->head.max_size = i->client_ranges[client].last;
+  else
+    m->head.max_size = 0;
   m->head.truncate_seq = i->truncate_seq;
   m->head.truncate_size = i->truncate_size;
   i->mtime.encode_timeval(&m->head.mtime);
index 0daaa005bd6ede05156076a08565bda2fcaffad7..d62e7f43709ae13d91bb649e8b205095e03e11a2 100644 (file)
@@ -339,7 +339,6 @@ private:
   {
     g_num_ino++;
     g_num_inoa++;
-    memset(&inode, 0, sizeof(inode));
     state = 0;  
     if (auth) state_set(STATE_AUTH);
   };
index 3183a7e76acc25c624ddab037d151caf4bee7275..45ea03a6a2e1b3cda580c1a1c187539469e82fef 100644 (file)
@@ -1126,7 +1126,7 @@ bool Locker::issue_caps(CInode *in, Capability *only_cap)
   // count conflicts with
   int nissued = 0;        
 
-  // should we increase max_size?
+  // should we increase client ranges?
   if (in->is_file() &&
       ((all_allowed|loner_allowed) & CEPH_CAP_FILE_WR) &&
       in->is_auth() &&
@@ -1235,7 +1235,8 @@ void Locker::issue_truncate(CInode *in)
 void Locker::revoke_stale_caps(Session *session)
 {
   dout(10) << "revoke_stale_caps for " << session->inst.name << dendl;
-  
+  int client = session->get_client();
+
   for (xlist<Capability*>::iterator p = session->caps.begin(); !p.end(); ++p) {
     Capability *cap = *p;
     cap->set_stale(true);
@@ -1245,7 +1246,7 @@ void Locker::revoke_stale_caps(Session *session)
       dout(10) << " revoking " << ccap_string(issued) << " on " << *in << dendl;      
       cap->revoke();
 
-      if (in->inode.max_size > in->inode.size)
+      if (in->inode.client_ranges.count(client))
        in->state_set(CInode::STATE_NEEDSRECOVER);
 
       if (!in->filelock.is_stable()) eval_gather(&in->filelock);
@@ -1417,22 +1418,34 @@ bool Locker::check_inode_max_size(CInode *in, bool force_wrlock, bool update_siz
   assert(in->is_auth());
 
   inode_t *latest = in->get_projected_inode();
-  uint64_t new_max = latest->max_size;
+  map<int,byte_range_t> new_ranges;
   __u64 size = latest->size;
   if (update_size)
     size = new_size;
-  
-  if ((in->get_caps_wanted() & (CEPH_CAP_FILE_WR|CEPH_CAP_FILE_BUFFER)) == 0)
-    new_max = 0;
-  else if ((size << 1) >= latest->max_size)
-    new_max = latest->max_size ? (latest->max_size << 1):in->get_layout_size_increment();
+  bool new_max = false;
 
-  if (new_max == latest->max_size && !update_size)
-    return false;  // no change.
+  // increase ranges as appropriate.
+  // shrink to 0 if no WR|BUFFER caps issued.
+  for (map<int,Capability*>::iterator p = in->client_caps.begin();
+       p != in->client_caps.end();
+       p++) {
+    if (p->second->issued() & (CEPH_CAP_FILE_WR|CEPH_CAP_FILE_BUFFER)) {
+      new_ranges[p->first].first = 0;
+      if (latest->client_ranges.count(p->first))
+       new_ranges[p->first].last = MAX(ROUND_UP_TO(size<<1, in->get_layout_size_increment()),
+                                       latest->client_ranges[p->first].last);
+      else
+       new_ranges[p->first].last = ROUND_UP_TO(size<<1, in->get_layout_size_increment());
+    }
+  }
+  if (latest->client_ranges != new_ranges)
+    new_max = true;
 
-  dout(10) << "check_inode_max_size " << latest->max_size << " -> " << new_max
-          << " on " << *in << dendl;
+  if (!update_size && !new_max)
+    return false;
 
+  dout(10) << "check_inode_max_size on " << *in << dendl;
+  
   if (!force_wrlock && !in->filelock.can_wrlock(in->get_loner())) {
     // lock?
     if (in->filelock.is_stable()) {
@@ -1454,10 +1467,14 @@ bool Locker::check_inode_max_size(CInode *in, bool force_wrlock, bool update_siz
     
   inode_t *pi = in->project_inode();
   pi->version = in->pre_dirty();
-  pi->max_size = new_max;
+
+  if (new_max) {
+    dout(10) << "check_inode_max_size client_ranges " << pi->client_ranges << " -> " << new_ranges << dendl;
+    pi->client_ranges = new_ranges;
+  }
+
   if (update_size) {
-    dout(10) << "check_inode_max_size also forcing size " 
-            << pi->size << " -> " << new_size << dendl;
+    dout(10) << "check_inode_max_size size " << pi->size << " -> " << new_size << dendl;
     pi->size = new_size;
     pi->rstat.rbytes = new_size;
   }
@@ -1514,7 +1531,7 @@ void Locker::share_inode_max_size(CInode *in)
     Capability *cap = it->second;
     if (cap->is_suppress())
       continue;
-    if (cap->pending() & (CEPH_CAP_GWR<<CEPH_CAP_SFILE)) {
+    if (cap->pending() & (CEPH_CAP_FILE_WR|CEPH_CAP_FILE_BUFFER)) {
       dout(10) << "share_inode_max_size with client" << client << dendl;
       MClientCaps *m = new MClientCaps(CEPH_CAP_OP_GRANT,
                                       in->ino(),
@@ -1781,7 +1798,6 @@ bool Locker::_do_cap_update(CInode *in, Capability *cap,
 {
   dout(10) << "_do_cap_update dirty " << ccap_string(dirty)
           << " wanted " << ccap_string(wanted)
-          << " max_size " << m->get_max_size()
           << " on " << *in << dendl;
   assert(in->is_auth());
   int client = m->get_source().num();
@@ -1790,27 +1806,30 @@ bool Locker::_do_cap_update(CInode *in, Capability *cap,
   // increase or zero max_size?
   __u64 size = m->get_size();
   bool change_max = false;
-  uint64_t new_max = latest->max_size;
+  uint64_t old_max = latest->client_ranges.count(client) ? latest->client_ranges[client].last : 0;
+  uint64_t new_max = old_max;
   
   if (in->is_file()) {
-    if (latest->max_size && (wanted & CEPH_CAP_ANY_FILE_WR) == 0) {
-      change_max = true;
-      new_max = 0;
-    }
-    else if ((wanted & CEPH_CAP_ANY_FILE_WR) &&
-            (size << 1) >= latest->max_size) {
-      dout(10) << " wr caps wanted, and size " << size
-              << " *2 >= max " << latest->max_size << ", increasing" << dendl;
-      change_max = true;
-      new_max = latest->max_size ? (latest->max_size << 1):in->get_layout_size_increment();
-    }
-    if ((wanted & CEPH_CAP_ANY_FILE_WR) &&
-       m->get_max_size() > new_max) {
-      dout(10) << "client requests file_max " << m->get_max_size()
-              << " > max " << latest->max_size << dendl;
-      change_max = true;
-      new_max = (m->get_max_size() << 1) & ~(in->get_layout_size_increment() - 1);
+    if (cap->issued() & CEPH_CAP_ANY_FILE_WR) {
+      if (m->get_max_size() > new_max) {
+       dout(10) << "client requests file_max " << m->get_max_size()
+                << " > max " << old_max << dendl;
+       change_max = true;
+       new_max = ROUND_UP_TO(m->get_max_size() << 1, in->get_layout_size_increment());
+      } else {
+       new_max = ROUND_UP_TO(size<<1, in->get_layout_size_increment());
+       if (new_max > old_max)
+         change_max = true;
+       else
+         new_max = old_max;
+      }
+    } else {
+      if (old_max) {
+       change_max = true;
+       new_max = 0;
+      }
     }
+
     if (change_max &&
        !in->filelock.can_wrlock(client)) {
       dout(10) << " i want to change file_max, but lock won't allow it (yet)" << dendl;
@@ -1893,9 +1912,13 @@ bool Locker::_do_cap_update(CInode *in, Capability *cap,
     }
   }
   if (change_max) {
-    dout(7) << "  max_size " << pi->max_size << " -> " << new_max
+    dout(7) << "  max_size " << old_max << " -> " << new_max
            << " for " << *in << dendl;
-    pi->max_size = new_max;
+    if (new_max) {
+      pi->client_ranges[client].first = 0;
+      pi->client_ranges[client].last = new_max;
+    } else 
+      pi->client_ranges.erase(client);
   }
     
   if (change_max || (dirty & (CEPH_CAP_FILE_EXCL|CEPH_CAP_FILE_WR))) 
index f65693b7a50fdfdf0ec143b1d06f558cc0d01cd6..58675dee6031a9a649b7377200eadda45aca9eba 100644 (file)
@@ -1307,8 +1307,8 @@ CInode *MDCache::cow_inode(CInode *in, snapid_t last)
   }
   if (oldin->is_any_caps())
     oldin->filelock.set_state(LOCK_LOCK);
-  else if (oldin->inode.max_size) {
-    dout(10) << "cow_inode WARNING max_size " << oldin->inode.max_size << " > 0 on " << *oldin << dendl;
+  else if (oldin->inode.client_ranges.size()) {
+    dout(10) << "cow_inode WARNING client_ranges " << oldin->inode.client_ranges << " on " << *oldin << dendl;
     //oldin->inode.max_size = 0;
   }
 
@@ -4317,7 +4317,7 @@ void MDCache::identify_files_to_recover()
     CInode *in = p->second;
     if (!in->is_auth())
       continue;
-    if (in->inode.max_size > in->inode.size) {
+    if (in->inode.client_ranges.size()) {
       in->filelock.set_state(LOCK_LOCK);
       in->loner_cap = -1;
       q.push_back(in);
@@ -4346,15 +4346,18 @@ void MDCache::do_file_recover()
     CInode *in = *file_recover_queue.begin();
     file_recover_queue.erase(in);
 
-    if (in->inode.max_size > in->inode.size) {
-      dout(10) << "do_file_recover starting " << in->inode.size << "/" << in->inode.max_size 
+    if (in->inode.client_ranges.size()) {
+      dout(10) << "do_file_recover starting " << in->inode.size << " " << in->inode.client_ranges
               << " " << *in << dendl;
       file_recovering.insert(in);
+      
+      __u64 max = in->inode.get_max_size();
+
       mds->filer->probe(in->inode.ino, &in->inode.layout, in->last,
-                       in->inode.max_size, &in->inode.size, &in->inode.mtime, false,
+                       max, &in->inode.size, &in->inode.mtime, false,
                        0, new C_MDC_Recover(this, in));    
     } else {
-      dout(10) << "do_file_recover skipping " << in->inode.size << "/" << in->inode.max_size 
+      dout(10) << "do_file_recover skipping " << in->inode.size
               << " " << *in << dendl;
       in->state_clear(CInode::STATE_NEEDSRECOVER);
       in->auth_unpin(this);
@@ -5166,8 +5169,13 @@ void MDCache::remove_client_cap(CInode *in, int client)
 {
   in->remove_client_cap(client);
 
-  if (!in->is_auth())
+  if (in->is_auth()) {
+    // make sure we clear out the client byte range
+    if (in->get_projected_inode()->client_ranges.count(client))
+      mds->locker->check_inode_max_size(in);
+  } else {
     mds->locker->request_inode_file_caps(in);
+  }
   
   mds->locker->eval(in, CEPH_CAP_LOCKS);
 
@@ -6794,7 +6802,7 @@ void MDCache::purge_stray(CDentry *dn)
     assert(in->last == CEPH_NOSNAP);
   }
 
-  __u64 to = MAX(in->inode.size, in->inode.max_size);
+  __u64 to = MAX(in->inode.size, in->inode.get_max_size());
   dout(10) << "purge_stray 0~" << to << " snapc " << snapc << " on " << *in << dendl;
   if (to)
     mds->filer->remove(in->inode.ino, &in->inode.layout, *snapc,
index 7fbec09a6f149ea93667efff83bf9966a557e7f8..94f406f57b3f35970575973ff0cac1634aa08330 100644 (file)
@@ -502,7 +502,6 @@ void Server::handle_client_reconnect(MClientReconnect *m)
        
        // mark client caps stale.
        inode_t fake_inode;
-       memset(&fake_inode, 0, sizeof(fake_inode));
        fake_inode.ino = p->first;
        MClientCaps *stale = new MClientCaps(CEPH_CAP_OP_EXPORT, p->first, 0, 0, 0);
        //stale->head.migrate_seq = 0; // FIXME ******
@@ -2030,6 +2029,7 @@ public:
 void Server::handle_client_openc(MDRequest *mdr)
 {
   MClientRequest *req = mdr->client_request;
+  int client = mdr->get_client();
 
   dout(7) << "open w/ O_CREAT on " << req->get_filepath() << dendl;
   
@@ -2074,7 +2074,10 @@ void Server::handle_client_openc(MDRequest *mdr)
 
   in->inode.mode = req->head.args.open.mode | S_IFREG;
   in->inode.version = dn->pre_dirty();
-  in->inode.max_size = (cmode & CEPH_FILE_MODE_WR) ? in->get_layout_size_increment() : 0;
+  if (cmode & CEPH_FILE_MODE_WR) {
+    in->inode.client_ranges[client].first = 0;
+    in->inode.client_ranges[client].last = in->get_layout_size_increment();
+  }
   in->inode.rstat.rfiles = 1;
 
   dn->first = in->first = follows+1;
index 44d3eb592ee56b9d45e914521645d6b190da08c4..caf56a90a1883d7e9c4dd815537b75cc11cbdd12 100644 (file)
@@ -20,7 +20,7 @@ using namespace std;
 
 #include <boost/pool/pool.hpp>
 
-#define CEPH_FS_ONDISK_MAGIC "ceph fs volume v004"
+#define CEPH_FS_ONDISK_MAGIC "ceph fs volume v005"
 
 
 //#define MDS_REF_SET      // define me for improved debug output, sanity checking
@@ -116,7 +116,7 @@ struct frag_info_t {
   __s64 size() const { return nfiles + nsubdirs; }
 
   void zero() {
-    memset(this, 0, sizeof(*this));
+    *this = frag_info_t();
   }
 
   // *this += cur - acc; acc = cur
@@ -175,7 +175,7 @@ struct nest_info_t {
                  ranchors(0), rsnaprealms(0) {}
 
   void zero() {
-    memset(this, 0, sizeof(*this));
+    *this = nest_info_t();
   }
 
   void sub(const nest_info_t &other) {
@@ -288,6 +288,28 @@ inline ostream& operator<<(ostream &out, const vinodeno_t &vino) {
 }
 
 
+struct byte_range_t {
+  __u64 first, last;    // interval client can write to
+
+  void encode(bufferlist &bl) const {
+    ::encode(first, bl);
+    ::encode(last, bl);
+  }
+  void decode(bufferlist::iterator& bl) {
+    ::decode(first, bl);
+    ::decode(last, bl);
+  }    
+};
+WRITE_CLASS_ENCODER(byte_range_t)
+
+inline ostream& operator<<(ostream& out, const byte_range_t& r)
+{
+  return out << r.first << '-' << r.last;
+}
+inline bool operator==(const byte_range_t& l, const byte_range_t& r) {
+  return l.first == r.first && l.last == r.last;
+}
+
 struct inode_t {
   // base (immutable)
   inodeno_t ino;
@@ -308,13 +330,14 @@ struct inode_t {
   // file (data access)
   ceph_file_layout layout;
   uint64_t   size;        // on directory, # dentries
-  uint64_t   max_size;    // client(s) are auth to write this much...
   uint32_t   truncate_seq;
   uint64_t   truncate_size, truncate_from;
   utime_t    mtime;   // file data modify time.
   utime_t    atime;   // file data access time.
   uint32_t   time_warp_seq;  // count of (potential) mtime/atime timewarps (i.e., utimes())
 
+  map<int,byte_range_t> client_ranges;  // client(s) can write to these ranges
+
   // dirfrag, recursive accountin
   frag_info_t dirstat;
   nest_info_t rstat, accounted_rstat;
@@ -324,6 +347,15 @@ struct inode_t {
   version_t file_data_version; // auth only
   version_t xattr_version;
 
+  inode_t() : ino(0), rdev(0),
+             mode(0), uid(0), gid(0),
+             nlink(0), anchored(false),
+             size(0), truncate_seq(0), truncate_size(0), truncate_from(0),
+             time_warp_seq(0),
+             version(0), file_data_version(0), xattr_version(0) { 
+    memset(&layout, 0, sizeof(layout));
+  }
+
   // file type
   bool is_symlink() const { return (mode & S_IFMT) == S_IFLNK; }
   bool is_dir()     const { return (mode & S_IFMT) == S_IFDIR; }
@@ -331,6 +363,26 @@ struct inode_t {
 
   bool is_truncating() const { return truncate_size != -1ull; }
 
+  __u64 get_max_size() const {
+    __u64 max = 0;
+      for (map<int,byte_range_t>::const_iterator p = client_ranges.begin();
+          p != client_ranges.end();
+          p++)
+       if (p->second.last > max)
+         max = p->second.last;
+      return max;
+  }
+  void set_max_size(__u64 new_max) {
+    if (new_max == 0) {
+      client_ranges.clear();
+    } else {
+      for (map<int,byte_range_t>::iterator p = client_ranges.begin();
+          p != client_ranges.end();
+          p++)
+       p->second.last = new_max;
+    }
+  }
+
   void encode(bufferlist &bl) const {
     ::encode(ino, bl);
     ::encode(rdev, bl);
@@ -345,13 +397,13 @@ struct inode_t {
 
     ::encode(layout, bl);
     ::encode(size, bl);
-    ::encode(max_size, bl);
     ::encode(truncate_seq, bl);
     ::encode(truncate_size, bl);
     ::encode(truncate_from, bl);
     ::encode(mtime, bl);
     ::encode(atime, bl);
     ::encode(time_warp_seq, bl);
+    ::encode(client_ranges, bl);
 
     ::encode(dirstat, bl);
     ::encode(rstat, bl);
@@ -375,13 +427,13 @@ struct inode_t {
 
     ::decode(layout, p);
     ::decode(size, p);
-    ::decode(max_size, p);
     ::decode(truncate_seq, p);
     ::decode(truncate_size, p);
     ::decode(truncate_from, p);
     ::decode(mtime, p);
     ::decode(atime, p);
     ::decode(time_warp_seq, p);
+    ::decode(client_ranges, p);
     
     ::decode(dirstat, p);
     ::decode(rstat, p);
index 1520a2c026486683139634392011b2023fdc9456..e07977348adfdf610439faa91c9e1062431fe733 100644 (file)
@@ -143,7 +143,6 @@ struct InodeStat {
     nlink = e.nlink;
     rdev = e.rdev;
 
-    memset(&dirstat, 0, sizeof(dirstat));
     dirstat.nfiles = e.files;
     dirstat.nsubdirs = e.subdirs;