A B
SharedBlobSet::lookup()
takes lock
nref is not 0
SharedBlob::put()
--nref
returns SharedBlobRef,
++nref
takes cache lock
SharedBlobSet::remove
takes lock
removes
deletes SharedBlob
-> A ends up with a ref to deleted SharedBlob
Fix by verifying that nref is still zero in SharedBlobSet::remove(),
while we are holding the SharedBlobSet::lock. The lock ensures that we
have increased the ref for the lookup before entering remove, so we can
verify that nref is still zero before removing it. If not, we have
raced, and put() bails out and does nothing.
Fixes: http://tracker.ceph.com/issues/36526
Signed-off-by: Sage Weil <sage@redhat.com>
(cherry picked from commit
020bd7b5f38a82d9eef5e25e6f4a4dd12b066915)
Conflicts:
src/os/bluestore/BlueStore.h
if (coll_snap != coll) {
goto again;
}
- coll_snap->shared_blob_set.remove(this);
-
+ if (!coll_snap->shared_blob_set.remove(this, true)) {
+ // race with lookup
+ return;
+ }
bc._clear(coll_snap->cache);
coll_snap->cache->rm_blob();
}
sb->coll = coll;
}
- void remove(SharedBlob *sb) {
+ bool remove(SharedBlob *sb, bool verify_nref_is_zero=false) {
std::lock_guard<std::mutex> l(lock);
assert(sb->get_parent() == this);
+ if (verify_nref_is_zero && sb->nref != 0) {
+ return false;
+ }
// only remove if it still points to us
auto p = sb_map.find(sb->get_sbid());
if (p != sb_map.end() &&
p->second == sb) {
sb_map.erase(p);
}
+ return true;
}
bool empty() {