]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
os/bluestore: avoid premature onode release. 43770/head
authorIgor Fedotov <ifed@suse.com>
Tue, 2 Nov 2021 12:03:39 +0000 (15:03 +0300)
committerIgor Fedotov <ifedotov@suse.com>
Tue, 14 Dec 2021 14:54:23 +0000 (17:54 +0300)
This was observed when onode's removal is followed by reading
and the latter causes object release before the removal is finalized.
The root cause is an improper 'pinned' state assessment in Onode::get

More detailed overview is:
At some point Onode::get() might face the case when nref == 2 and pinned = true
which means parallel incomplete put is running on the onode - ref count is
decremented but pinned state is still unmodified (and even lock hasn't been
acquired yet).
This might finally result in two puts racing over the same onode with nref == 2
which finally results in a premature onode release:
  // nref =3, pinned = 1
  // Thread 1                   Thread 2
  //   o->put()                   o->get()
  //   --nref(n = 2, pinned=1)
  //                              nref++ (n=3, pinned = 1)
  //                              return
  //                              ...
  //                              o->put()
  //                              --nref(n = 2)
  //                              pinned = 0,
  //                              --nref(n = 1)
  //                              ocs->_unpin_and_rm(o) -> o->put()
  //                                ...
  //                                --nref(n = 0)
  //                                release o
  //  o->c->get_onode_cache()
  //  FAULT!
  //
The suggested fix is to introduce additional atomic counter tracking
running put() functions. And permit onode release when both regular
nref and put_nref are both equal to zero.

Fixes: https://tracker.ceph.com/issues/53002
Signed-off-by: Igor Fedotov <igor.fedotov@croit.io>
src/os/bluestore/BlueStore.cc
src/os/bluestore/BlueStore.h

index 465417ac3ae2026e9c3b92578e213243dfc67a44..cdbef602f2e1ac14e800c9e8b7fb4f9f13e3c73a 100644 (file)
@@ -3660,6 +3660,7 @@ void BlueStore::Onode::get() {
   }
 }
 void BlueStore::Onode::put() {
+  ++put_nref;
   int n = --nref;
   if (n == 2) {
     OnodeCacheShard* ocs = c->get_onode_cache();
@@ -3679,19 +3680,18 @@ void BlueStore::Onode::put() {
         ocs->_unpin(this);
       } else {
         ocs->_unpin_and_rm(this);
-        // remove will also decrement nref and delete Onode
+        // remove will also decrement nref
         c->onode_map._remove(oid);
       }
     }
     // additional decrement for newly unpinned instance
-    // should be the last action since Onode can be released
-    // at any point after this decrement
     if (need_unpin) {
-      n = --nref;
+      --nref;
     }
     ocs->lock.unlock();
   }
-  if (n == 0) {
+  auto pn = --put_nref;
+  if (nref == 0 && pn == 0) {
     delete this;
   }
 }
index ee991cd8a2f18a3667529ae85e63352bff7458fa..d59295a8796dfc240d4a059f93af6153499b12a2 100644 (file)
@@ -1120,6 +1120,7 @@ public:
     MEMPOOL_CLASS_HELPERS();
 
     std::atomic_int nref;  ///< reference count
+    std::atomic_int put_nref = {0};
     Collection *c;
     ghobject_t oid;