]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/os/bluestore/BlueStore.cc
update sources to 12.2.2
[ceph.git] / ceph / src / os / bluestore / BlueStore.cc
index 3566005cd01a76132fdbf37d97e6bb10764e80bb..5fe5b98c5dd074980957be21127afc53bb07d029 100644 (file)
@@ -1615,6 +1615,12 @@ bool BlueStore::OnodeSpace::map_any(std::function<bool(OnodeRef)> f)
   return false;
 }
 
+void BlueStore::OnodeSpace::dump(CephContext *cct, int lvl)
+{
+  for (auto& i : onode_map) {
+    ldout(cct, lvl) << i.first << " : " << i.second << dendl;
+  }
+}
 
 // SharedBlob
 
@@ -1661,7 +1667,7 @@ void BlueStore::SharedBlob::put()
                             << " removing self from set " << get_parent()
                             << dendl;
     if (get_parent()) {
-      if (get_parent()->remove(this)) {
+      if (get_parent()->try_remove(this)) {
        delete this;
       } else {
        ldout(coll->store->cct, 20)
@@ -1692,6 +1698,19 @@ void BlueStore::SharedBlob::put_ref(uint64_t offset, uint32_t length,
   }
 }
 
+// SharedBlobSet
+
+#undef dout_prefix
+#define dout_prefix *_dout << "bluestore.sharedblobset(" << this << ") "
+
+void BlueStore::SharedBlobSet::dump(CephContext *cct, int lvl)
+{
+  std::lock_guard<std::mutex> l(lock);
+  for (auto& i : sb_map) {
+    ldout(cct, lvl) << i.first << " : " << *i.second << dendl;
+  }
+}
+
 // Blob
 
 #undef dout_prefix
@@ -1976,6 +1995,7 @@ void BlueStore::ExtentMap::update(KeyValueDB::Transaction t,
       unsigned n;
       // we need to encode inline_bl to measure encoded length
       bool never_happen = encode_some(0, OBJECT_MAX_SIZE, inline_bl, &n);
+      inline_bl.reassign_to_mempool(mempool::mempool_bluestore_cache_other);
       assert(!never_happen);
       size_t len = inline_bl.length();
       dout(20) << __func__ << "  inline shard " << len << " bytes from " << n
@@ -3228,12 +3248,17 @@ BlueStore::OnodeRef BlueStore::Collection::get_onode(
     on->exists = true;
     bufferptr::iterator p = v.front().begin_deep();
     on->onode.decode(p);
+    for (auto& i : on->onode.attrs) {
+      i.second.reassign_to_mempool(mempool::mempool_bluestore_cache_other);
+    }
 
     // initialize extent_map
     on->extent_map.decode_spanning_blobs(p);
     if (on->onode.extent_map_shards.empty()) {
       denc(on->extent_map.inline_bl, p);
       on->extent_map.decode_some(on->extent_map.inline_bl);
+      on->extent_map.inline_bl.reassign_to_mempool(
+       mempool::mempool_bluestore_cache_other);
     } else {
       on->extent_map.init_shards(false, false);
     }
@@ -3292,13 +3317,13 @@ void BlueStore::Collection::split_cache(
          continue;
        }
        ldout(store->cct, 20) << __func__ << "  moving " << *sb << dendl;
-       sb->coll = dest;
        if (sb->get_sbid()) {
          ldout(store->cct, 20) << __func__
                                << "   moving registration " << *sb << dendl;
          shared_blob_set.remove(sb);
          dest->shared_blob_set.add(dest, sb);
        }
+       sb->coll = dest;
        if (dest->cache != cache) {
          for (auto& i : sb->bc.buffer_map) {
            if (!i.second->is_writing()) {
@@ -3631,8 +3656,8 @@ void BlueStore::_set_compression()
     return;
   }
 
-  if (cct->_conf->bluestore_compression_max_blob_size) {
-    comp_min_blob_size = cct->_conf->bluestore_compression_max_blob_size;
+  if (cct->_conf->bluestore_compression_min_blob_size) {
+    comp_min_blob_size = cct->_conf->bluestore_compression_min_blob_size;
   } else {
     assert(bdev);
     if (bdev->is_rotational()) {
@@ -3774,6 +3799,36 @@ int BlueStore::_set_cache_sizes()
   return 0;
 }
 
+int BlueStore::write_meta(const std::string& key, const std::string& value)
+{
+  bluestore_bdev_label_t label;
+  string p = path + "/block";
+  int r = _read_bdev_label(cct, p, &label);
+  if (r < 0) {
+    return ObjectStore::write_meta(key, value);
+  }
+  label.meta[key] = value;
+  r = _write_bdev_label(cct, p, label);
+  assert(r == 0);
+  return ObjectStore::write_meta(key, value);
+}
+
+int BlueStore::read_meta(const std::string& key, std::string *value)
+{
+  bluestore_bdev_label_t label;
+  string p = path + "/block";
+  int r = _read_bdev_label(cct, p, &label);
+  if (r < 0) {
+    return ObjectStore::read_meta(key, value);
+  }
+  auto i = label.meta.find(key);
+  if (i == label.meta.end()) {
+    return ObjectStore::read_meta(key, value);
+  }
+  *value = i->second;
+  return 0;
+}
+
 void BlueStore::_init_logger()
 {
   PerfCountersBuilder b(cct, "bluestore",
@@ -3966,7 +4021,8 @@ void BlueStore::_close_path()
   path_fd = -1;
 }
 
-int BlueStore::_write_bdev_label(string path, bluestore_bdev_label_t label)
+int BlueStore::_write_bdev_label(CephContext *cct,
+                                string path, bluestore_bdev_label_t label)
 {
   dout(10) << __func__ << " path " << path << " label " << label << dendl;
   bufferlist bl;
@@ -3990,6 +4046,11 @@ int BlueStore::_write_bdev_label(string path, bluestore_bdev_label_t label)
     derr << __func__ << " failed to write to " << path
         << ": " << cpp_strerror(r) << dendl;
   }
+  r = ::fsync(fd);
+  if (r < 0) {
+    derr << __func__ << " failed to fsync " << path
+        << ": " << cpp_strerror(r) << dendl;
+  }
   VOID_TEMP_FAILURE_RETRY(::close(fd));
   return r;
 }
@@ -4047,7 +4108,7 @@ int BlueStore::_check_or_set_bdev_label(
     label.size = size;
     label.btime = ceph_clock_now();
     label.description = desc;
-    int r = _write_bdev_label(path, label);
+    int r = _write_bdev_label(cct, path, label);
     if (r < 0)
       return r;
   } else {
@@ -4159,18 +4220,19 @@ int BlueStore::_open_fm(bool create)
       bl.append(freelist_type);
       t->set(PREFIX_SUPER, "freelist_type", bl);
     }
-    fm->create(bdev->get_size(), t);
+    fm->create(bdev->get_size(), min_alloc_size, t);
 
     // allocate superblock reserved space.  note that we do not mark
     // bluefs space as allocated in the freelist; we instead rely on
     // bluefs_extents.
-    fm->allocate(0, SUPER_RESERVED, t);
+    uint64_t reserved = ROUND_UP_TO(MAX(SUPER_RESERVED, min_alloc_size),
+                                   min_alloc_size);
+    fm->allocate(0, reserved, t);
 
-    uint64_t reserved = 0;
     if (cct->_conf->bluestore_bluefs) {
       assert(bluefs_extents.num_intervals() == 1);
       interval_set<uint64_t>::iterator p = bluefs_extents.begin();
-      reserved = p.get_start() + p.get_len();
+      reserved = ROUND_UP_TO(p.get_start() + p.get_len(), min_alloc_size);
       dout(20) << __func__ << " reserved 0x" << std::hex << reserved << std::dec
               << " for bluefs" << dendl;
       bufferlist bl;
@@ -4178,8 +4240,6 @@ int BlueStore::_open_fm(bool create)
       t->set(PREFIX_SUPER, "bluefs_extents", bl);
       dout(20) << __func__ << " bluefs_extents 0x" << std::hex << bluefs_extents
               << std::dec << dendl;
-    } else {
-      reserved = SUPER_RESERVED;
     }
 
     if (cct->_conf->bluestore_debug_prefill > 0) {
@@ -4226,7 +4286,7 @@ int BlueStore::_open_fm(bool create)
     db->submit_transaction_sync(t);
   }
 
-  int r = fm->init();
+  int r = fm->init(bdev->get_size());
   if (r < 0) {
     derr << __func__ << " freelist init failed: " << cpp_strerror(r) << dendl;
     delete fm;
@@ -4493,7 +4553,9 @@ int BlueStore::_open_db(bool create)
     string bfn;
     struct stat st;
 
-    bfn = path + "/block.db";
+    if (read_meta("path_block.db", &bfn) < 0) {
+      bfn = path + "/block.db";
+    }
     if (::stat(bfn.c_str(), &st) == 0) {
       r = bluefs->add_block_device(BlueFS::BDEV_DB, bfn);
       if (r < 0) {
@@ -4532,7 +4594,9 @@ int BlueStore::_open_db(bool create)
     }
 
     // shared device
-    bfn = path + "/block";
+    if (read_meta("path_block", &bfn) < 0) {
+      bfn = path + "/block";
+    }
     r = bluefs->add_block_device(bluefs_shared_bdev, bfn);
     if (r < 0) {
       derr << __func__ << " add block device(" << bfn << ") returned: " 
@@ -4545,6 +4609,13 @@ int BlueStore::_open_db(bool create)
        bdev->get_size() * (cct->_conf->bluestore_bluefs_min_ratio +
                            cct->_conf->bluestore_bluefs_gift_ratio);
       initial = MAX(initial, cct->_conf->bluestore_bluefs_min);
+      if (cct->_conf->bluefs_alloc_size % min_alloc_size) {
+       derr << __func__ << " bluefs_alloc_size 0x" << std::hex
+            << cct->_conf->bluefs_alloc_size << " is not a multiple of "
+            << "min_alloc_size 0x" << min_alloc_size << std::dec << dendl;
+       r = -EINVAL;
+       goto free_bluefs;
+      }
       // align to bluefs's alloc_size
       initial = P2ROUNDUP(initial, cct->_conf->bluefs_alloc_size);
       // put bluefs in the middle of the device in case it is an HDD
@@ -4554,7 +4625,9 @@ int BlueStore::_open_db(bool create)
       bluefs_extents.insert(start, initial);
     }
 
-    bfn = path + "/block.wal";
+    if (read_meta("path_block.wal", &bfn) < 0) {
+      bfn = path + "/block.wal";
+    }
     if (::stat(bfn.c_str(), &st) == 0) {
       r = bluefs->add_block_device(BlueFS::BDEV_WAL, bfn);
       if (r < 0) {
@@ -4817,9 +4890,11 @@ int BlueStore::_balance_bluefs_freespace(PExtentVector *extents)
             << " > max_ratio " << cct->_conf->bluestore_bluefs_max_ratio
             << ", should reclaim " << pretty_si_t(reclaim) << dendl;
   }
+
+  // don't take over too much of the freespace
+  uint64_t free_cap = cct->_conf->bluestore_bluefs_max_ratio * total_free;
   if (bluefs_total < cct->_conf->bluestore_bluefs_min &&
-    cct->_conf->bluestore_bluefs_min <
-      (uint64_t)(cct->_conf->bluestore_bluefs_max_ratio * total_free)) {
+      cct->_conf->bluestore_bluefs_min < free_cap) {
     uint64_t g = cct->_conf->bluestore_bluefs_min - bluefs_total;
     dout(10) << __func__ << " bluefs_total " << bluefs_total
             << " < min " << cct->_conf->bluestore_bluefs_min
@@ -4828,6 +4903,17 @@ int BlueStore::_balance_bluefs_freespace(PExtentVector *extents)
       gift = g;
     reclaim = 0;
   }
+  uint64_t min_free = cct->_conf->get_val<uint64_t>("bluestore_bluefs_min_free");
+  if (bluefs_free < min_free &&
+      min_free < free_cap) {
+    uint64_t g = min_free - bluefs_free;
+    dout(10) << __func__ << " bluefs_free " << bluefs_total
+            << " < min " << min_free
+            << ", should gift " << pretty_si_t(g) << dendl;
+    if (g > gift)
+      gift = g;
+    reclaim = 0;
+  }
 
   if (gift) {
     // round up to alloc size
@@ -5150,6 +5236,39 @@ int BlueStore::mkfs()
   if (r < 0)
     goto out_close_fsid;
 
+  {
+    string wal_path = cct->_conf->get_val<string>("bluestore_block_wal_path");
+    if (wal_path.size()) {
+      write_meta("path_block.wal", wal_path);
+    }
+    string db_path = cct->_conf->get_val<string>("bluestore_block_db_path");
+    if (db_path.size()) {
+      write_meta("path_block.db", db_path);
+    }
+  }
+
+  // choose min_alloc_size
+  if (cct->_conf->bluestore_min_alloc_size) {
+    min_alloc_size = cct->_conf->bluestore_min_alloc_size;
+  } else {
+    assert(bdev);
+    if (bdev->is_rotational()) {
+      min_alloc_size = cct->_conf->bluestore_min_alloc_size_hdd;
+    } else {
+      min_alloc_size = cct->_conf->bluestore_min_alloc_size_ssd;
+    }
+  }
+
+  // make sure min_alloc_size is power of 2 aligned.
+  if (!ISP2(min_alloc_size)) {
+    derr << __func__ << " min_alloc_size 0x"
+        << std::hex << min_alloc_size << std::dec
+        << " is not power of 2 aligned!"
+        << dendl;
+    r = -EINVAL;
+    goto out_close_bdev;
+  }
+
   r = _open_db(true);
   if (r < 0)
     goto out_close_bdev;
@@ -5167,28 +5286,6 @@ int BlueStore::mkfs()
       t->set(PREFIX_SUPER, "blobid_max", bl);
     }
 
-    // choose min_alloc_size
-    if (cct->_conf->bluestore_min_alloc_size) {
-      min_alloc_size = cct->_conf->bluestore_min_alloc_size;
-    } else {
-      assert(bdev);
-      if (bdev->is_rotational()) {
-       min_alloc_size = cct->_conf->bluestore_min_alloc_size_hdd;
-      } else {
-       min_alloc_size = cct->_conf->bluestore_min_alloc_size_ssd;
-      }
-    }
-
-    // make sure min_alloc_size is power of 2 aligned.
-    if (!ISP2(min_alloc_size)) {
-      derr << __func__ << " min_alloc_size 0x"
-           << std::hex << min_alloc_size << std::dec
-           << " is not power of 2 aligned!"
-           << dendl;
-      r = -EINVAL;
-      goto out_close_fm;
-    }
-
     {
       bufferlist bl;
       ::encode((uint64_t)min_alloc_size, bl);
@@ -5205,7 +5302,7 @@ int BlueStore::mkfs()
   if (r < 0)
     goto out_close_fm;
 
-  r = write_meta("bluefs", stringify((int)cct->_conf->bluestore_bluefs));
+  r = write_meta("bluefs", stringify(bluefs ? 1 : 0));
   if (r < 0)
     goto out_close_fm;
 
@@ -5268,6 +5365,8 @@ int BlueStore::_mount(bool kv_only)
 {
   dout(1) << __func__ << " path " << path << dendl;
 
+  _kv_only = kv_only;
+
   {
     string type;
     int r = read_meta("type", &type);
@@ -5378,23 +5477,24 @@ int BlueStore::_mount(bool kv_only)
 
 int BlueStore::umount()
 {
-  assert(mounted);
+  assert(_kv_only || mounted);
   dout(1) << __func__ << dendl;
 
   _osr_drain_all();
   _osr_unregister_all();
 
-  mempool_thread.shutdown();
-
-  dout(20) << __func__ << " stopping kv thread" << dendl;
-  _kv_stop();
-  _reap_collections();
-  _flush_cache();
-  dout(20) << __func__ << " closing" << dendl;
-
   mounted = false;
-  _close_alloc();
-  _close_fm();
+  if (!_kv_only) {
+    mempool_thread.shutdown();
+    dout(20) << __func__ << " stopping kv thread" << dendl;
+    _kv_stop();
+    _reap_collections();
+    _flush_cache();
+    dout(20) << __func__ << " closing" << dendl;
+
+    _close_alloc();
+    _close_fm();
+  }
   _close_db();
   _close_bdev();
   _close_fsid();
@@ -5445,7 +5545,7 @@ int BlueStore::_fsck_check_extents(
     }
     bool already = false;
     apply(
-      e.offset, e.length, block_size, used_blocks, __func__,
+      e.offset, e.length, min_alloc_size, used_blocks, __func__,
       [&](uint64_t pos, mempool_dynamic_bitset &bs) {
        if (bs.test(pos))
          already = true;
@@ -5466,10 +5566,13 @@ int BlueStore::_fsck_check_extents(
   return errors;
 }
 
-int BlueStore::fsck(bool deep)
+int BlueStore::_fsck(bool deep, bool repair)
 {
-  dout(1) << __func__ << (deep ? " (deep)" : " (shallow)") << " start" << dendl;
+  dout(1) << __func__
+         << (repair ? " fsck" : " repair")
+         << (deep ? " (deep)" : " (shallow)") << " start" << dendl;
   int errors = 0;
+  int repaired = 0;
 
   typedef btree::btree_set<
     uint64_t,std::less<uint64_t>,
@@ -5547,9 +5650,10 @@ int BlueStore::fsck(bool deep)
   if (r < 0)
     goto out_scan;
 
-  used_blocks.resize(bdev->get_size() / block_size);
+  used_blocks.resize(bdev->get_size() / min_alloc_size);
   apply(
-    0, SUPER_RESERVED, block_size, used_blocks, "0~SUPER_RESERVED",
+    0, MAX(min_alloc_size, SUPER_RESERVED), min_alloc_size, used_blocks,
+    "0~SUPER_RESERVED",
     [&](uint64_t pos, mempool_dynamic_bitset &bs) {
       bs.set(pos);
     }
@@ -5558,7 +5662,7 @@ int BlueStore::fsck(bool deep)
   if (bluefs) {
     for (auto e = bluefs_extents.begin(); e != bluefs_extents.end(); ++e) {
       apply(
-        e.get_start(), e.get_len(), block_size, used_blocks, "bluefs",
+        e.get_start(), e.get_len(), min_alloc_size, used_blocks, "bluefs",
         [&](uint64_t pos, mempool_dynamic_bitset &bs) {
           bs.set(pos);
         }
@@ -5593,7 +5697,7 @@ int BlueStore::fsck(bool deep)
       if (is_extent_shard_key(it->key())) {
        while (!expecting_shards.empty() &&
               expecting_shards.front() < it->key()) {
-         derr << __func__ << " error: missing shard key "
+         derr << "fsck error: missing shard key "
               << pretty_binary_string(expecting_shards.front())
               << dendl;
          ++errors;
@@ -5609,18 +5713,18 @@ int BlueStore::fsck(bool deep)
         uint32_t offset;
         string okey;
         get_key_extent_shard(it->key(), &okey, &offset);
-        derr << __func__ << " error: stray shard 0x" << std::hex << offset
+        derr << "fsck error: stray shard 0x" << std::hex << offset
             << std::dec << dendl;
         if (expecting_shards.empty()) {
-          derr << __func__ << " error: " << pretty_binary_string(it->key())
+          derr << "fsck error: " << pretty_binary_string(it->key())
                << " is unexpected" << dendl;
           ++errors;
           continue;
         }
        while (expecting_shards.front() > it->key()) {
-         derr << __func__ << " error:   saw " << pretty_binary_string(it->key())
+         derr << "fsck error:   saw " << pretty_binary_string(it->key())
               << dendl;
-         derr << __func__ << " error:   exp "
+         derr << "fsck error:   exp "
               << pretty_binary_string(expecting_shards.front()) << dendl;
          ++errors;
          expecting_shards.pop_front();
@@ -5634,7 +5738,7 @@ int BlueStore::fsck(bool deep)
       ghobject_t oid;
       int r = get_key_object(it->key(), &oid);
       if (r < 0) {
-        derr << __func__ << " error: bad object key "
+        derr << "fsck error: bad object key "
              << pretty_binary_string(it->key()) << dendl;
        ++errors;
        continue;
@@ -5654,7 +5758,7 @@ int BlueStore::fsck(bool deep)
          }
        }
        if (!c) {
-          derr << __func__ << " error: stray object " << oid
+          derr << "fsck error: stray object " << oid
                << " not owned by any collection" << dendl;
          ++errors;
          continue;
@@ -5665,7 +5769,7 @@ int BlueStore::fsck(bool deep)
 
       if (!expecting_shards.empty()) {
        for (auto &k : expecting_shards) {
-         derr << __func__ << " error: missing shard key "
+         derr << "fsck error: missing shard key "
               << pretty_binary_string(k) << dendl;
        }
        ++errors;
@@ -5677,12 +5781,12 @@ int BlueStore::fsck(bool deep)
       OnodeRef o = c->get_onode(oid, false);
       if (o->onode.nid) {
        if (o->onode.nid > nid_max) {
-         derr << __func__ << " error: " << oid << " nid " << o->onode.nid
+         derr << "fsck error: " << oid << " nid " << o->onode.nid
               << " > nid_max " << nid_max << dendl;
          ++errors;
        }
        if (used_nids.count(o->onode.nid)) {
-         derr << __func__ << " error: " << oid << " nid " << o->onode.nid
+         derr << "fsck error: " << oid << " nid " << o->onode.nid
               << " already in use" << dendl;
          ++errors;
          continue; // go for next object
@@ -5704,7 +5808,7 @@ int BlueStore::fsck(bool deep)
        get_extent_shard_key(o->key, s.shard_info->offset,
                             &expecting_shards.back());
        if (s.shard_info->offset >= o->onode.size) {
-         derr << __func__ << " error: " << oid << " shard 0x" << std::hex
+         derr << "fsck error: " << oid << " shard 0x" << std::hex
               << s.shard_info->offset << " past EOF at 0x" << o->onode.size
               << std::dec << dendl;
          ++errors;
@@ -5718,14 +5822,14 @@ int BlueStore::fsck(bool deep)
       for (auto& l : o->extent_map.extent_map) {
        dout(20) << __func__ << "    " << l << dendl;
        if (l.logical_offset < pos) {
-         derr << __func__ << " error: " << oid << " lextent at 0x"
+         derr << "fsck error: " << oid << " lextent at 0x"
               << std::hex << l.logical_offset
               << " overlaps with the previous, which ends at 0x" << pos
               << std::dec << dendl;
          ++errors;
        }
        if (o->extent_map.spans_shard(l.logical_offset, l.length)) {
-         derr << __func__ << " error: " << oid << " lextent at 0x"
+         derr << "fsck error: " << oid << " lextent at 0x"
               << std::hex << l.logical_offset << "~" << l.length
               << " spans a shard boundary"
               << std::dec << dendl;
@@ -5771,7 +5875,7 @@ int BlueStore::fsck(bool deep)
                 << std::dec << " for " << *i.first << dendl;
        const bluestore_blob_t& blob = i.first->get_blob();
        if (i.second & blob.unused) {
-         derr << __func__ << " error: " << oid << " blob claims unused 0x"
+         derr << "fsck error: " << oid << " blob claims unused 0x"
               << std::hex << blob.unused
               << " but extents reference 0x" << i.second
               << " on blob " << *i.first << dendl;
@@ -5793,7 +5897,7 @@ int BlueStore::fsck(bool deep)
            if ((blob.unused & mask) == mask) {
              // this csum chunk region is marked unused
              if (blob.get_csum_item(p) != 0) {
-               derr << __func__ << " error: " << oid
+               derr << "fsck error: " << oid
                     << " blob claims csum chunk 0x" << std::hex << pos
                     << "~" << csum_chunk_size
                     << " is unused (mask 0x" << mask << " of unused 0x"
@@ -5811,7 +5915,7 @@ int BlueStore::fsck(bool deep)
        const bluestore_blob_t& blob = i.first->get_blob();
        bool equal = i.first->get_blob_use_tracker().equal(i.second);
        if (!equal) {
-         derr << __func__ << " error: " << oid << " blob " << *i.first
+         derr << "fsck error: " << oid << " blob " << *i.first
               << " doesn't match expected ref_map " << i.second << dendl;
          ++errors;
        }
@@ -5822,12 +5926,12 @@ int BlueStore::fsck(bool deep)
        }
        if (blob.is_shared()) {
          if (i.first->shared_blob->get_sbid() > blobid_max) {
-           derr << __func__ << " error: " << oid << " blob " << blob
+           derr << "fsck error: " << oid << " blob " << blob
                 << " sbid " << i.first->shared_blob->get_sbid() << " > blobid_max "
                 << blobid_max << dendl;
            ++errors;
          } else if (i.first->shared_blob->get_sbid() == 0) {
-            derr << __func__ << " error: " << oid << " blob " << blob
+            derr << "fsck error: " << oid << " blob " << blob
                  << " marked as shared but has uninitialized sbid"
                  << dendl;
             ++errors;
@@ -5853,14 +5957,14 @@ int BlueStore::fsck(bool deep)
        int r = _do_read(c.get(), o, 0, o->onode.size, bl, 0);
        if (r < 0) {
          ++errors;
-         derr << __func__ << " error: " << oid << " error during read: "
+         derr << "fsck error: " << oid << " error during read: "
               << cpp_strerror(r) << dendl;
        }
       }
       // omap
       if (o->onode.has_omap()) {
        if (used_omap_head.count(o->onode.nid)) {
-         derr << __func__ << " error: " << oid << " omap_head " << o->onode.nid
+         derr << "fsck error: " << oid << " omap_head " << o->onode.nid
               << " already in use" << dendl;
          ++errors;
        } else {
@@ -5876,14 +5980,14 @@ int BlueStore::fsck(bool deep)
       string key = it->key();
       uint64_t sbid;
       if (get_key_shared_blob(key, &sbid)) {
-       derr << __func__ << " error: bad key '" << key
+       derr << "fsck error: bad key '" << key
             << "' in shared blob namespace" << dendl;
        ++errors;
        continue;
       }
       auto p = sb_info.find(sbid);
       if (p == sb_info.end()) {
-       derr << __func__ << " error: found stray shared blob data for sbid 0x"
+       derr << "fsck error: found stray shared blob data for sbid 0x"
             << std::hex << sbid << std::dec << dendl;
        ++errors;
       } else {
@@ -5895,7 +5999,7 @@ int BlueStore::fsck(bool deep)
        ::decode(shared_blob, blp);
        dout(20) << __func__ << "  " << *sbi.sb << " " << shared_blob << dendl;
        if (shared_blob.ref_map != sbi.ref_map) {
-         derr << __func__ << " error: shared blob 0x" << std::hex << sbid
+         derr << "fsck error: shared blob 0x" << std::hex << sbid
               << std::dec << " ref_map " << shared_blob.ref_map
               << " != expected " << sbi.ref_map << dendl;
          ++errors;
@@ -5913,12 +6017,12 @@ int BlueStore::fsck(bool deep)
     }
   }
   for (auto &p : sb_info) {
-    derr << __func__ << " error: shared_blob 0x" << p.first
+    derr << "fsck error: shared_blob 0x" << p.first
         << " key is missing (" << *p.second.sb << ")" << dendl;
     ++errors;
   }
   if (!(actual_statfs == expected_statfs)) {
-    derr << __func__ << " error: actual " << actual_statfs
+    derr << "fsck error: actual " << actual_statfs
         << " != expected " << expected_statfs << dendl;
     ++errors;
   }
@@ -5930,7 +6034,7 @@ int BlueStore::fsck(bool deep)
       uint64_t omap_head;
       _key_decode_u64(it->key().c_str(), &omap_head);
       if (used_omap_head.count(omap_head) == 0) {
-       derr << __func__ << " error: found stray omap data on omap_head "
+       derr << "fsck error: found stray omap data on omap_head "
             << omap_head << dendl;
        ++errors;
       }
@@ -5947,7 +6051,7 @@ int BlueStore::fsck(bool deep)
       try {
        ::decode(wt, p);
       } catch (buffer::error& e) {
-       derr << __func__ << " error: failed to decode deferred txn "
+       derr << "fsck error: failed to decode deferred txn "
             << pretty_binary_string(it->key()) << dendl;
        r = -EIO;
         goto out_scan;
@@ -5957,7 +6061,7 @@ int BlueStore::fsck(bool deep)
               << " released 0x" << std::hex << wt.released << std::dec << dendl;
       for (auto e = wt.released.begin(); e != wt.released.end(); ++e) {
         apply(
-          e.get_start(), e.get_len(), block_size, used_blocks, "deferred",
+          e.get_start(), e.get_len(), min_alloc_size, used_blocks, "deferred",
           [&](uint64_t pos, mempool_dynamic_bitset &bs) {
             bs.set(pos);
           }
@@ -5972,7 +6076,8 @@ int BlueStore::fsck(bool deep)
     // know they are allocated.
     for (auto e = bluefs_extents.begin(); e != bluefs_extents.end(); ++e) {
       apply(
-        e.get_start(), e.get_len(), block_size, used_blocks, "bluefs_extents",
+        e.get_start(), e.get_len(), min_alloc_size, used_blocks,
+        "bluefs_extents",
         [&](uint64_t pos, mempool_dynamic_bitset &bs) {
          bs.reset(pos);
         }
@@ -5983,7 +6088,7 @@ int BlueStore::fsck(bool deep)
     while (fm->enumerate_next(&offset, &length)) {
       bool intersects = false;
       apply(
-        offset, length, block_size, used_blocks, "free",
+        offset, length, min_alloc_size, used_blocks, "free",
         [&](uint64_t pos, mempool_dynamic_bitset &bs) {
           if (bs.test(pos)) {
             intersects = true;
@@ -5993,79 +6098,26 @@ int BlueStore::fsck(bool deep)
         }
       );
       if (intersects) {
-       derr << __func__ << " error: free extent 0x" << std::hex << offset
-            << "~" << length << std::dec
-            << " intersects allocated blocks" << dendl;
-       ++errors;
-      }
-    }
-    fm->enumerate_reset();
-    size_t count = used_blocks.count();
-    if (used_blocks.size() == count + 1) {
-      // this due to http://tracker.ceph.com/issues/21089
-      bufferlist fm_bpb_bl, fm_blocks_bl, fm_bpk_bl;
-      db->get(PREFIX_ALLOC, "bytes_per_block", &fm_bpb_bl);
-      db->get(PREFIX_ALLOC, "blocks", &fm_blocks_bl);
-      db->get(PREFIX_ALLOC, "blocks_per_key", &fm_bpk_bl);
-      uint64_t fm_blocks = 0;
-      uint64_t fm_bsize = 1;
-      uint64_t fm_blocks_per_key = 1;
-      try {
-       auto p = fm_blocks_bl.begin();
-       ::decode(fm_blocks, p);
-       auto q = fm_bpb_bl.begin();
-       ::decode(fm_bsize, q);
-       auto r = fm_bpk_bl.begin();
-       ::decode(fm_blocks_per_key, r);
-      } catch (buffer::error& e) {
-      }
-      uint64_t dev_bsize = bdev->get_block_size();
-      uint64_t bad_size = bdev->get_size() & ~fm_bsize;
-      if (used_blocks.test(bad_size / dev_bsize) == 0) {
-       // this is the last block of the device that we previously
-       // (incorrectly) truncated off of the effective device size.  this
-       // prevented BitmapFreelistManager from marking it as used along with
-       // the other "past-eof" blocks in the last key slot.  mark it used
-       // now.
-       derr << __func__ << " warning: fixing leaked block 0x" << std::hex
-            << bad_size << "~" << fm_bsize << std::dec << " due to old bug"
-            << dendl;
-       KeyValueDB::Transaction t = db->get_transaction();
-       // fix freelistmanager metadata (the internal 'blocks' count is
-       // rounded up to include the trailing key, past eof)
-       uint64_t new_blocks = bdev->get_size() / fm_bsize;
-       if (new_blocks / fm_blocks_per_key * fm_blocks_per_key != new_blocks) {
-         new_blocks = (new_blocks / fm_blocks_per_key + 1) *
-           fm_blocks_per_key;
-       }
-       if (new_blocks != fm_blocks) {
-         // the fm block count increased
-         derr << __func__ << "  freelist block and key count changed, fixing 0x"
-              << std::hex << bdev->get_size() << "~"
-              << ((new_blocks * fm_bsize) - bdev->get_size()) << std::dec
-              << dendl;
-         bufferlist bl;
-         ::encode(new_blocks, bl);
-         t->set(PREFIX_ALLOC, "blocks", bl);
-         fm->allocate(bdev->get_size(),
-                      (new_blocks * fm_bsize) - bdev->get_size(),
-                      t);
+       if (offset == SUPER_RESERVED &&
+           length == min_alloc_size - SUPER_RESERVED) {
+         // this is due to the change just after luminous to min_alloc_size
+         // granularity allocations, and our baked in assumption at the top
+         // of _fsck that 0~ROUND_UP_TO(SUPER_RESERVED,min_alloc_size) is used
+         // (vs luminous's ROUND_UP_TO(SUPER_RESERVED,block_size)).  harmless,
+         // since we will never allocate this region below min_alloc_size.
+         dout(10) << __func__ << " ignoring free extent between SUPER_RESERVED"
+                  << " and min_alloc_size, 0x" << std::hex << offset << "~"
+                  << length << dendl;
        } else {
-         // block count is the same, but size changed; fix just the size
-         derr << __func__ << "  fixing just the stray block at 0x"
-              << std::hex << bad_size << "~" << fm_bsize << std::dec << dendl;
-         fm->allocate(bad_size, fm_bsize, t);
+         derr << "fsck error: free extent 0x" << std::hex << offset
+              << "~" << length << std::dec
+              << " intersects allocated blocks" << dendl;
+         ++errors;
        }
-       bufferlist sizebl;
-       ::encode(bdev->get_size(), sizebl);
-       t->set(PREFIX_ALLOC, "size", sizebl);
-       int r = db->submit_transaction_sync(t);
-       assert(r == 0);
-
-       used_blocks.set(bad_size / dev_bsize);
-       ++count;
       }
     }
+    fm->enumerate_reset();
+    size_t count = used_blocks.count();
     if (used_blocks.size() != count) {
       assert(used_blocks.size() > count);
       ++errors;
@@ -6076,9 +6128,9 @@ int BlueStore::fsck(bool deep)
        while (true) {
          size_t next = used_blocks.find_next(cur);
          if (next != cur + 1) {
-           derr << __func__ << " error: leaked extent 0x" << std::hex
-                << ((uint64_t)start * block_size) << "~"
-                << ((cur + 1 - start) * block_size) << std::dec
+           derr << "fsck error: leaked extent 0x" << std::hex
+                << ((uint64_t)start * min_alloc_size) << "~"
+                << ((cur + 1 - start) * min_alloc_size) << std::dec
                 << dendl;
            start = next;
            break;
@@ -6121,9 +6173,10 @@ int BlueStore::fsck(bool deep)
          << dendl;
 
   utime_t duration = ceph_clock_now() - start;
-  dout(1) << __func__ << " finish with " << errors << " errors in "
+  dout(1) << __func__ << " finish with " << errors << " errors, " << repaired
+         << " repaired, " << (errors - repaired) << " remaining in "
          << duration << " seconds" << dendl;
-  return errors;
+  return errors - repaired;
 }
 
 void BlueStore::collect_metadata(map<string,string> *pm)
@@ -8772,6 +8825,14 @@ void BlueStore::_deferred_submit_unlock(OpSequencer *osr)
   bdev->aio_submit(&b->ioc);
 }
 
+struct C_DeferredTrySubmit : public Context {
+  BlueStore *store;
+  C_DeferredTrySubmit(BlueStore *s) : store(s) {}
+  void finish(int r) {
+    store->deferred_try_submit();
+  }
+};
+
 void BlueStore::_deferred_aio_finish(OpSequencer *osr)
 {
   dout(10) << __func__ << " osr " << osr << dendl;
@@ -8788,9 +8849,7 @@ void BlueStore::_deferred_aio_finish(OpSequencer *osr)
       deferred_queue.erase(q);
     } else if (deferred_aggressive) {
       dout(20) << __func__ << " queuing async deferred_try_submit" << dendl;
-      deferred_finisher.queue(new FunctionContext([&](int) {
-           deferred_try_submit();
-         }));
+      deferred_finisher.queue(new C_DeferredTrySubmit(this));
     } else {
       dout(20) << __func__ << " leaving queued, more pending" << dendl;
     }
@@ -8930,6 +8989,11 @@ int BlueStore::queue_transactions(
               << dendl;
       ++deferred_aggressive;
       deferred_try_submit();
+      {
+       // wake up any previously finished deferred events
+       std::lock_guard<std::mutex> l(kv_lock);
+       kv_cond.notify_one();
+      }
       throttle_deferred_bytes.get(txc->cost);
       --deferred_aggressive;
    }
@@ -9856,20 +9920,10 @@ int BlueStore::_do_alloc_write(
   dout(20) << __func__ << " txc " << txc
           << " " << wctx->writes.size() << " blobs"
           << dendl;
-
-  uint64_t need = 0;
-  auto max_bsize = MAX(wctx->target_blob_size, min_alloc_size);
-  for (auto &wi : wctx->writes) {
-    need += wi.blob_length;
-  }
-  int r = alloc->reserve(need);
-  if (r < 0) {
-    derr << __func__ << " failed to reserve 0x" << std::hex << need << std::dec
-        << dendl;
-    return r;
+  if (wctx->writes.empty()) {
+    return 0;
   }
 
-  uint64_t hint = 0;
   CompressorRef c;
   double crr = 0;
   if (wctx->compress) {
@@ -9894,7 +9948,7 @@ int BlueStore::_do_alloc_write(
       cct->_conf->bluestore_compression_required_ratio,
       [&]() {
         double val;
-        if(coll->pool_opts.get(pool_opts_t::COMPRESSION_REQUIRED_RATIO, &val)) {
+        if (coll->pool_opts.get(pool_opts_t::COMPRESSION_REQUIRED_RATIO, &val)) {
           return boost::optional<double>(val);
         }
         return boost::optional<double>();
@@ -9909,78 +9963,102 @@ int BlueStore::_do_alloc_write(
     csum,
     [&]() {
       int val;
-      if(coll->pool_opts.get(pool_opts_t::CSUM_TYPE, &val)) {
+      if (coll->pool_opts.get(pool_opts_t::CSUM_TYPE, &val)) {
         return  boost::optional<int>(val);
       }
       return boost::optional<int>();
     }
   );
 
+  // compress (as needed) and calc needed space
+  uint64_t need = 0;
+  auto max_bsize = MAX(wctx->target_blob_size, min_alloc_size);
   for (auto& wi : wctx->writes) {
-    BlobRef b = wi.b;
-    bluestore_blob_t& dblob = b->dirty_blob();
-    uint64_t b_off = wi.b_off;
-    bufferlist *l = &wi.bl;
-    uint64_t final_length = wi.blob_length;
-    uint64_t csum_length = wi.blob_length;
-    unsigned csum_order = block_size_order;
-    bufferlist compressed_bl;
-    bool compressed = false;
-    if(c && wi.blob_length > min_alloc_size) {
-
+    if (c && wi.blob_length > min_alloc_size) {
       utime_t start = ceph_clock_now();
 
       // compress
-      assert(b_off == 0);
-      assert(wi.blob_length == l->length());
-      bluestore_compression_header_t chdr;
-      chdr.type = c->get_type();
+      assert(wi.b_off == 0);
+      assert(wi.blob_length == wi.bl.length());
+
       // FIXME: memory alignment here is bad
       bufferlist t;
-
-      r = c->compress(*l, t);
+      int r = c->compress(wi.bl, t);
       assert(r == 0);
 
+      bluestore_compression_header_t chdr;
+      chdr.type = c->get_type();
       chdr.length = t.length();
-      ::encode(chdr, compressed_bl);
-      compressed_bl.claim_append(t);
-      uint64_t rawlen = compressed_bl.length();
-      uint64_t newlen = P2ROUNDUP(rawlen, min_alloc_size);
-      uint64_t want_len_raw = final_length * crr;
+      ::encode(chdr, wi.compressed_bl);
+      wi.compressed_bl.claim_append(t);
+
+      wi.compressed_len = wi.compressed_bl.length();
+      uint64_t newlen = P2ROUNDUP(wi.compressed_len, min_alloc_size);
+      uint64_t want_len_raw = wi.blob_length * crr;
       uint64_t want_len = P2ROUNDUP(want_len_raw, min_alloc_size);
-      if (newlen <= want_len && newlen < final_length) {
-        // Cool. We compressed at least as much as we were hoping to.
-        // pad out to min_alloc_size
-       compressed_bl.append_zero(newlen - rawlen);
-       logger->inc(l_bluestore_write_pad_bytes, newlen - rawlen);
+      if (newlen <= want_len && newlen < wi.blob_length) {
+       // Cool. We compressed at least as much as we were hoping to.
+       // pad out to min_alloc_size
+       wi.compressed_bl.append_zero(newlen - wi.compressed_len);
+       logger->inc(l_bluestore_write_pad_bytes, newlen - wi.compressed_len);
        dout(20) << __func__ << std::hex << "  compressed 0x" << wi.blob_length
-                << " -> 0x" << rawlen << " => 0x" << newlen
+                << " -> 0x" << wi.compressed_len << " => 0x" << newlen
                 << " with " << c->get_type()
                 << std::dec << dendl;
-       txc->statfs_delta.compressed() += rawlen;
-       txc->statfs_delta.compressed_original() += l->length();
+       txc->statfs_delta.compressed() += wi.compressed_len;
+       txc->statfs_delta.compressed_original() += wi.blob_length;
        txc->statfs_delta.compressed_allocated() += newlen;
-       l = &compressed_bl;
-       final_length = newlen;
-       csum_length = newlen;
-       csum_order = ctz(newlen);
-       dblob.set_compressed(wi.blob_length, rawlen);
-       compressed = true;
-        logger->inc(l_bluestore_compress_success_count);
+       logger->inc(l_bluestore_compress_success_count);
+       wi.compressed = true;
+       need += newlen;
       } else {
-       dout(20) << __func__ << std::hex << "  0x" << l->length()
-                << " compressed to 0x" << rawlen << " -> 0x" << newlen
-                 << " with " << c->get_type()
-                 << ", which is more than required 0x" << want_len_raw
+       dout(20) << __func__ << std::hex << "  0x" << wi.blob_length
+                << " compressed to 0x" << wi.compressed_len << " -> 0x" << newlen
+                << " with " << c->get_type()
+                << ", which is more than required 0x" << want_len_raw
                 << " -> 0x" << want_len
-                 << ", leaving uncompressed"
-                 << std::dec << dendl;
-        logger->inc(l_bluestore_compress_rejected_count);
+                << ", leaving uncompressed"
+                << std::dec << dendl;
+       logger->inc(l_bluestore_compress_rejected_count);
+       need += wi.blob_length;
       }
       logger->tinc(l_bluestore_compress_lat,
                   ceph_clock_now() - start);
+    } else {
+      need += wi.blob_length;
     }
-    if (!compressed && wi.new_blob) {
+  }
+  int r = alloc->reserve(need);
+  if (r < 0) {
+    derr << __func__ << " failed to reserve 0x" << std::hex << need << std::dec
+        << dendl;
+    return r;
+  }
+  AllocExtentVector prealloc;
+  prealloc.reserve(2 * wctx->writes.size());;
+  int prealloc_left = 0;
+  prealloc_left = alloc->allocate(
+    need, min_alloc_size, need,
+    0, &prealloc);
+  assert(prealloc_left == (int64_t)need);
+  dout(20) << __func__ << " prealloc " << prealloc << dendl;
+  auto prealloc_pos = prealloc.begin();
+
+  for (auto& wi : wctx->writes) {
+    BlobRef b = wi.b;
+    bluestore_blob_t& dblob = b->dirty_blob();
+    uint64_t b_off = wi.b_off;
+    bufferlist *l = &wi.bl;
+    uint64_t final_length = wi.blob_length;
+    uint64_t csum_length = wi.blob_length;
+    unsigned csum_order = block_size_order;
+    if (wi.compressed) {
+      final_length = wi.compressed_bl.length();
+      csum_length = final_length;
+      csum_order = ctz(csum_length);
+      l = &wi.compressed_bl;
+      dblob.set_compressed(wi.blob_length, wi.compressed_len);
+    } else if (wi.new_blob) {
       // initialize newly created blob only
       assert(dblob.is_mutable());
       if (l->length() != wi.blob_length) {
@@ -10015,17 +10093,27 @@ int BlueStore::_do_alloc_write(
     }
 
     AllocExtentVector extents;
-    extents.reserve(4);  // 4 should be (more than) enough for most allocations
-    int64_t got = alloc->allocate(final_length, min_alloc_size, 
-                                 max_alloc_size.load(),
-                                 hint, &extents);
-    assert(got == (int64_t)final_length);
-    need -= got;
-    txc->statfs_delta.allocated() += got;
+    int64_t left = final_length;
+    while (left > 0) {
+      assert(prealloc_left > 0);
+      if (prealloc_pos->length <= left) {
+       prealloc_left -= prealloc_pos->length;
+       left -= prealloc_pos->length;
+       txc->statfs_delta.allocated() += prealloc_pos->length;
+       extents.push_back(*prealloc_pos);
+       ++prealloc_pos;
+      } else {
+       extents.emplace_back(prealloc_pos->offset, left);
+       prealloc_pos->offset += left;
+       prealloc_pos->length -= left;
+       prealloc_left -= left;
+       txc->statfs_delta.allocated() += left;
+       left = 0;
+       break;
+      }
+    }
     for (auto& p : extents) {
-      bluestore_pextent_t e = bluestore_pextent_t(p);
-      txc->allocated.insert(e.offset, e.length);
-      hint = p.end();
+      txc->allocated.insert(p.offset, p.length);
     }
     dblob.allocated(P2ALIGN(b_off, min_alloc_size), final_length, extents);
 
@@ -10079,9 +10167,8 @@ int BlueStore::_do_alloc_write(
       }
     }
   }
-  if (need > 0) {
-    alloc->unreserve(need);
-  }
+  assert(prealloc_pos == prealloc.end());
+  assert(prealloc_left == 0);
   return 0;
 }
 
@@ -10607,10 +10694,14 @@ int BlueStore::_do_remove(
     if (b.is_shared() &&
        sb->loaded &&
        maybe_unshared_blobs.count(sb)) {
-      b.map(e.blob_offset, e.length, [&](uint64_t off, uint64_t len) {
-        expect[sb].get(off, len);
-       return 0;
-      });
+      if (b.is_compressed()) {
+       expect[sb].get(0, b.get_ondisk_length());
+      } else {
+       b.map(e.blob_offset, e.length, [&](uint64_t off, uint64_t len) {
+           expect[sb].get(off, len);
+           return 0;
+         });
+      }
     }
   }
 
@@ -10671,10 +10762,14 @@ int BlueStore::_setattr(TransContext *txc,
           << " " << name << " (" << val.length() << " bytes)"
           << dendl;
   int r = 0;
-  if (val.is_partial())
-    o->onode.attrs[name.c_str()] = bufferptr(val.c_str(), val.length());
-  else
-    o->onode.attrs[name.c_str()] = val;
+  if (val.is_partial()) {
+    auto& b = o->onode.attrs[name.c_str()] = bufferptr(val.c_str(),
+                                                      val.length());
+    b.reassign_to_mempool(mempool::mempool_bluestore_cache_other);
+  } else {
+    auto& b = o->onode.attrs[name.c_str()] = val;
+    b.reassign_to_mempool(mempool::mempool_bluestore_cache_other);
+  }
   txc->write_onode(o);
   dout(10) << __func__ << " " << c->cid << " " << o->oid
           << " " << name << " (" << val.length() << " bytes)"
@@ -10693,11 +10788,14 @@ int BlueStore::_setattrs(TransContext *txc,
   int r = 0;
   for (map<string,bufferptr>::const_iterator p = aset.begin();
        p != aset.end(); ++p) {
-    if (p->second.is_partial())
-      o->onode.attrs[p->first.c_str()] =
+    if (p->second.is_partial()) {
+      auto& b = o->onode.attrs[p->first.c_str()] =
        bufferptr(p->second.c_str(), p->second.length());
-    else
-      o->onode.attrs[p->first.c_str()] = p->second;
+      b.reassign_to_mempool(mempool::mempool_bluestore_cache_other);
+    } else {
+      auto& b = o->onode.attrs[p->first.c_str()] = p->second;
+      b.reassign_to_mempool(mempool::mempool_bluestore_cache_other);
+    }
   }
   txc->write_onode(o);
   dout(10) << __func__ << " " << c->cid << " " << o->oid
@@ -11551,6 +11649,14 @@ void BlueStore::_flush_cache()
     assert(i->empty());
   }
   for (auto& p : coll_map) {
+    if (!p.second->onode_map.empty()) {
+      derr << __func__ << "stray onodes on " << p.first << dendl;
+      p.second->onode_map.dump(cct, 0);
+    }
+    if (!p.second->shared_blob_set.empty()) {
+      derr << __func__ << " stray shared blobs on " << p.first << dendl;
+      p.second->shared_blob_set.dump(cct, 0);
+    }
     assert(p.second->onode_map.empty());
     assert(p.second->shared_blob_set.empty());
   }