]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mds: fix corner case of replaying open sessions
authorYan, Zheng <zyan@redhat.com>
Sat, 8 Jun 2019 05:08:21 +0000 (13:08 +0800)
committerYan, Zheng <zyan@redhat.com>
Sat, 8 Jun 2019 13:45:07 +0000 (21:45 +0800)
Marking a session dirty may flush all existing dirty sessions. MDS
calls Server::finish_force_open_sessions() for log event that opens
multiple sessions. The function marks sessions dirty one by one. So
sessions opened by a log event may get flushed partially.

When replaying a log event that opens multiple sessions, mds need to
check if some of these sessions have already been flushed.

Fixes: https://tracker.ceph.com/issues/40211
Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
src/mds/Server.cc
src/mds/SessionMap.cc
src/mds/SessionMap.h
src/mds/journal.cc

index d4392d1b604339d85b36e2fe235694d0cf7dad5d..deaf69a8a32ba8eb0fd71eb8188db6ee6fc4ac37 100644 (file)
@@ -3200,7 +3200,7 @@ void Server::apply_allocated_inos(MDRequestRef& mdr, Session *session)
     ceph_assert(session);
     session->pending_prealloc_inos.subtract(mdr->prealloc_inos);
     session->info.prealloc_inos.insert(mdr->prealloc_inos);
-    mds->sessionmap.mark_dirty(session);
+    mds->sessionmap.mark_dirty(session, !mdr->used_prealloc_ino);
     mds->inotable->apply_alloc_ids(mdr->prealloc_inos);
   }
   if (mdr->used_prealloc_ino) {
index 144b2607484c4eb5b690c8674b94edd82d207b67..3a9f465418f5808a9c790db2a53f9061fe4e35b6 100644 (file)
@@ -669,12 +669,13 @@ void SessionMap::touch_session(Session *session)
   session->last_cap_renew = clock::now();
 }
 
-void SessionMap::_mark_dirty(Session *s)
+void SessionMap::_mark_dirty(Session *s, bool may_save)
 {
   if (dirty_sessions.count(s->info.inst.name))
     return;
 
-  if (dirty_sessions.size() >= g_conf()->mds_sessionmap_keys_per_op) {
+  if (may_save &&
+      dirty_sessions.size() >= g_conf()->mds_sessionmap_keys_per_op) {
     // Pre-empt the usual save() call from journal segment trim, in
     // order to avoid building up an oversized OMAP update operation
     // from too many sessions modified at once
@@ -685,12 +686,12 @@ void SessionMap::_mark_dirty(Session *s)
   dirty_sessions.insert(s->info.inst.name);
 }
 
-void SessionMap::mark_dirty(Session *s)
+void SessionMap::mark_dirty(Session *s, bool may_save)
 {
   dout(20) << __func__ << " s=" << s << " name=" << s->info.inst.name
     << " v=" << version << dendl;
 
-  _mark_dirty(s);
+  _mark_dirty(s, may_save);
   version++;
   s->pop_pv(version);
 }
@@ -700,7 +701,7 @@ void SessionMap::replay_dirty_session(Session *s)
   dout(20) << __func__ << " s=" << s << " name=" << s->info.inst.name
     << " v=" << version << dendl;
 
-  _mark_dirty(s);
+  _mark_dirty(s, false);
 
   replay_advance_version();
 }
index abcaab679d04562a801b5183a1e8156f564c0a2c..add18631049099c8bc916748f1e137bbec7743d9 100644 (file)
@@ -656,8 +656,14 @@ public:
     get_client_sessions(f);
   }
 
-  void replay_open_sessions(map<client_t,entity_inst_t>& client_map,
+  void replay_open_sessions(version_t event_cmapv,
+                           map<client_t,entity_inst_t>& client_map,
                            map<client_t,client_metadata_t>& client_metadata_map) {
+    // Server::finish_force_open_sessions() marks sessions dirty one by one.
+    // Marking a session dirty may flush all existing dirty sessions. So it's
+    // possible that some sessions are already saved in sessionmap.
+    ceph_assert(version + client_map.size() >= event_cmapv);
+    unsigned already_saved = client_map.size() - (event_cmapv - version);
     for (map<client_t,entity_inst_t>::iterator p = client_map.begin(); 
         p != client_map.end(); 
         ++p) {
@@ -666,6 +672,12 @@ public:
       if (q != client_metadata_map.end())
        s->info.client_metadata.merge(q->second);
 
+      if (already_saved > 0) {
+       ceph_assert(s->is_open());
+       --already_saved;
+       continue;
+      }
+
       set_state(s, Session::STATE_OPEN);
       replay_dirty_session(s);
     }
@@ -718,7 +730,7 @@ protected:
   std::set<entity_name_t> dirty_sessions;
   std::set<entity_name_t> null_sessions;
   bool loaded_legacy = false;
-  void _mark_dirty(Session *session);
+  void _mark_dirty(Session *session, bool may_save);
 public:
 
   /**
@@ -729,7 +741,7 @@ public:
    * to the backing store.  Must have called
    * mark_projected previously for this session.
    */
-  void mark_dirty(Session *session);
+  void mark_dirty(Session *session, bool may_save=true);
 
   /**
    * Advance the projected version, and mark this
index d44c4ab1949c5ac9549f302b3b6c7e059eeec919..0888725985e6415bd1c2ac8e2023b44c3daec1a9 100644 (file)
@@ -1778,8 +1778,7 @@ void ESessions::replay(MDSRank *mds)
   } else {
     dout(10) << "ESessions.replay sessionmap " << mds->sessionmap.get_version()
             << " < " << cmapv << dendl;
-    mds->sessionmap.replay_open_sessions(client_map, client_metadata_map);
-    ceph_assert(mds->sessionmap.get_version() == cmapv);
+    mds->sessionmap.replay_open_sessions(cmapv, client_map, client_metadata_map);
   }
   update_segment();
 }
@@ -2062,9 +2061,7 @@ void EUpdate::replay(MDSRank *mds)
       decode(cm, blp);
       if (!blp.end())
        decode(cmm, blp);
-      mds->sessionmap.replay_open_sessions(cm, cmm);
-
-      ceph_assert(mds->sessionmap.get_version() == cmapv);
+      mds->sessionmap.replay_open_sessions(cmapv, cm, cmm);
     }
   }
   update_segment();
@@ -2910,15 +2907,7 @@ void EImportStart::replay(MDSRank *mds)
     decode(cm, blp);
     if (!blp.end())
       decode(cmm, blp);
-    mds->sessionmap.replay_open_sessions(cm, cmm);
-
-    if (mds->sessionmap.get_version() != cmapv) {
-      derr << "sessionmap version " << mds->sessionmap.get_version()
-           << " != cmapv " << cmapv << dendl;
-      mds->clog->error() << "failure replaying journal (EImportStart)";
-      mds->damaged();
-      ceph_abort();  // Should be unreachable because damaged() calls respawn()
-    }
+    mds->sessionmap.replay_open_sessions(cmapv, cm, cmm);
   }
   update_segment();
 }