]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
os/bluestore: allow main device expansion when DB needs space to
authorIgor Fedotov <ifedotov@suse.com>
Fri, 20 Mar 2020 17:01:03 +0000 (20:01 +0300)
committerIgor Fedotov <ifedotov@suse.com>
Wed, 25 Mar 2020 21:59:35 +0000 (00:59 +0300)
recover.

This patch provides a fix for an expansion "deadlock" state:
OSD is out of space at main disk and DB needs to recover and
flush some additional data to the disk. To expand the main device one needs
to alter freelist parameters in DB which in turn triggers DB recovery.
The following approach is used to fix that:
1) Major set of Freelist Manager's key parameters
(primarily actual size and block count) is kept at device label from now on.
2)Secondary replica of parameters set is preserved at DB mostly to permit
downgrades if any.
3) At first expansion procedure updates bdev label only. No updates to DB takes
place hence read-only mode for DB is enough to execute it.

4) On regular BlueStore mount (second stage of expantion does this as
well) DB is initially loaded in read-only.

5)Freelist manager is started and initiated with parameters from device label.
Original allocation bits are taken from DB, new ones (appeared after
expansion but not present in DB) are virtually treated by FM as free.
Allocator is initialed with expanded size accordingly.

6) DB is reopened in full access mode. During this process BlueFS is able
to request for additional space from main device. This still doesn't need
DB update as BlueFS tracks owned extents via internal logging, not
persisting this info at DB
Hence DB recovery if any completes successfully. DB is fully operational
at this point.

7) Freelist Manager sync function is called to
commit all the changes: expanded size, location bits adjustment for
old and new boundaries.
Regular allocations that needs DB update are operational at this point.
Store is ready.

Signed-off-by: Igor Fedotov <ifedotov@suse.com>
src/os/bluestore/BitmapFreelistManager.cc
src/os/bluestore/BitmapFreelistManager.h
src/os/bluestore/BlueStore.cc
src/os/bluestore/BlueStore.h
src/os/bluestore/FreelistManager.h

index 203fe4e6571c8c27db69c08e1aee476b2c501ee9..3f75c30dea289a13a5d9b83aa1369332d85aecc3 100644 (file)
@@ -4,6 +4,7 @@
 #include "BitmapFreelistManager.h"
 #include "kv/KeyValueDB.h"
 #include "os/kv.h"
+#include "include/stringify.h"
 
 #include "common/debug.h"
 
@@ -73,9 +74,8 @@ int BitmapFreelistManager::create(uint64_t new_size, uint64_t granularity,
 
   _init_misc();
 
-  blocks = size / bytes_per_block;
-  if (blocks / blocks_per_key * blocks_per_key != blocks) {
-    blocks = (blocks / blocks_per_key + 1) * blocks_per_key;
+  blocks = size_2_block_count(size);
+  if (blocks * bytes_per_block > size) {
     dout(10) << __func__ << " rounding blocks up from 0x" << std::hex << size
             << " to 0x" << (blocks * bytes_per_block)
             << " (0x" << blocks << " blocks)" << std::dec << dendl;
@@ -111,28 +111,29 @@ int BitmapFreelistManager::create(uint64_t new_size, uint64_t granularity,
   return 0;
 }
 
-int BitmapFreelistManager::expand(uint64_t new_size, KeyValueDB::Transaction txn)
+int BitmapFreelistManager::_expand(uint64_t old_size, KeyValueDB* db)
 {
-  assert(new_size > size);
+  assert(old_size < size);
   ceph_assert(isp2(bytes_per_block));
 
-  uint64_t blocks0 = size / bytes_per_block;
-  if (blocks0 / blocks_per_key * blocks_per_key != blocks0) {
-    blocks0 = (blocks0 / blocks_per_key + 1) * blocks_per_key;
-    dout(10) << __func__ << " rounding blocks up from 0x" << std::hex << size
-            << " to 0x" << (blocks0 * bytes_per_block)
+  KeyValueDB::Transaction txn;
+  txn = db->get_transaction();
+
+  auto blocks0 = size_2_block_count(old_size);
+  if (blocks0 * bytes_per_block > old_size) {
+    dout(10) << __func__ << " rounding1 blocks up from 0x" << std::hex
+             << old_size << " to 0x" << (blocks0 * bytes_per_block)
             << " (0x" << blocks0 << " blocks)" << std::dec << dendl;
-    // reset previous past-eof blocks to unallocated
-    _xor(size, blocks0 * bytes_per_block - size, txn);
+    // reset past-eof blocks to unallocated
+    _xor(old_size, blocks0 * bytes_per_block - old_size, txn);
   }
 
-  size = p2align(new_size, bytes_per_block);
-  blocks = size / bytes_per_block;
+  size = p2align(size, bytes_per_block);
+  blocks = size_2_block_count(size);
 
-  if (blocks / blocks_per_key * blocks_per_key != blocks) {
-    blocks = (blocks / blocks_per_key + 1) * blocks_per_key;
-    dout(10) << __func__ << " rounding blocks up from 0x" << std::hex << size
-            << " to 0x" << (blocks * bytes_per_block)
+  if (blocks * bytes_per_block > size) {
+    dout(10) << __func__ << " rounding2 blocks up from 0x" << std::hex
+             << size << " to 0x" << (blocks * bytes_per_block)
             << " (0x" << blocks << " blocks)" << std::dec << dendl;
     // set past-eof blocks as allocated
     _xor(size, blocks * bytes_per_block - size, txn);
@@ -154,13 +155,29 @@ int BitmapFreelistManager::expand(uint64_t new_size, KeyValueDB::Transaction txn
     encode(size, bl);
     txn->set(meta_prefix, "size", bl);
   }
+  db->submit_transaction_sync(txn);
+
   return 0;
 }
 
-int BitmapFreelistManager::init(KeyValueDB *kvdb)
+int BitmapFreelistManager::read_size_meta_from_db(KeyValueDB* kvdb,
+  uint64_t* res)
 {
-  dout(1) << __func__ << dendl;
+  bufferlist v;
+  int r = kvdb->get(meta_prefix, "size", &v);
+  if (r < 0) {
+    derr << __func__ << " missing size meta in DB" << dendl;
+    return ENOENT;
+  } else {
+    auto p = v.cbegin();
+    decode(*res, p);
+    r = 0;
+  }
+  return r;
+}
 
+void BitmapFreelistManager::_load_from_db(KeyValueDB* kvdb)
+{
   KeyValueDB::Iterator it = kvdb->get_iterator(meta_prefix);
   it->lower_bound(string());
 
@@ -172,31 +189,44 @@ int BitmapFreelistManager::init(KeyValueDB *kvdb)
       auto p = bl.cbegin();
       decode(bytes_per_block, p);
       dout(10) << __func__ << " bytes_per_block 0x" << std::hex
-              << bytes_per_block << std::dec << dendl;
+        << bytes_per_block << std::dec << dendl;
     } else if (k == "blocks") {
       bufferlist bl = it->value();
       auto p = bl.cbegin();
       decode(blocks, p);
       dout(10) << __func__ << " blocks 0x" << std::hex << blocks << std::dec
-              << dendl;
+        << dendl;
     } else if (k == "size") {
       bufferlist bl = it->value();
       auto p = bl.cbegin();
       decode(size, p);
       dout(10) << __func__ << " size 0x" << std::hex << size << std::dec
-              << dendl;
+        << dendl;
     } else if (k == "blocks_per_key") {
       bufferlist bl = it->value();
       auto p = bl.cbegin();
       decode(blocks_per_key, p);
       dout(10) << __func__ << " blocks_per_key 0x" << std::hex << blocks_per_key
-              << std::dec << dendl;
+        << std::dec << dendl;
     } else {
       derr << __func__ << " unrecognized meta " << k << dendl;
-      return -EIO;
     }
     it->next();
   }
+}
+
+
+int BitmapFreelistManager::init(const bluestore_bdev_label_t& label,
+  KeyValueDB *kvdb,
+  bool db_in_read_only)
+{
+  dout(1) << __func__ << dendl;
+  int r = _init_from_label(label);
+  if (r != 0) {
+    dout(1) << __func__ << " fall back to legacy meta repo" << dendl;
+    _load_from_db(kvdb);
+  }
+  _sync(kvdb, db_in_read_only);
 
   dout(10) << __func__ << std::hex
           << " size 0x" << size
@@ -208,6 +238,73 @@ int BitmapFreelistManager::init(KeyValueDB *kvdb)
   return 0;
 }
 
+int BitmapFreelistManager::_init_from_label(const bluestore_bdev_label_t& label)
+{
+  dout(1) << __func__ << dendl;
+
+  int r = ENOENT;
+  string err;
+
+  auto it = label.meta.find("bfm_size");
+  auto end = label.meta.end();
+  if (it != end) {
+    size = strict_iecstrtoll(it->second.c_str(), &err);
+    if (!err.empty()) {
+      derr << __func__ << " Failed to parse - "
+        << it->first << ":" << it->second
+        << ", error: " << err << dendl;
+      return r;
+    }
+  } else {
+    // this is expected for legacy deployed OSDs
+    dout(0) << __func__ << " bfm_size not found in bdev meta" << dendl;
+    return r;
+  }
+
+  it = label.meta.find("bfm_blocks");
+  if (it != end) {
+    blocks = strict_iecstrtoll(it->second.c_str(), &err);
+    if (!err.empty()) {
+      derr << __func__ << " Failed to parse - "
+        << it->first << ":" << it->second
+        << ", error: " << err << dendl;
+      return r;
+    }
+  } else {
+    derr << __func__ << " bfm_blocks not found in bdev meta" << dendl;
+    return r;
+  }
+
+  it = label.meta.find("bfm_bytes_per_block");
+  if (it != end) {
+    bytes_per_block = strict_iecstrtoll(it->second.c_str(), &err);
+    if (!err.empty()) {
+      derr << __func__ << " Failed to parse - "
+        << it->first << ":" << it->second
+        << ", error: " << err << dendl;
+      return r;
+    }
+  } else {
+    derr << __func__ << " bfm_bytes_per_block not found in bdev meta" << dendl;
+    return r;
+  }
+  it = label.meta.find("bfm_blocks_per_key");
+  if (it != end) {
+    blocks_per_key = strict_iecstrtoll(it->second.c_str(), &err);
+    if (!err.empty()) {
+      derr << __func__ << " Failed to parse - "
+        << it->first << ":" << it->second
+        << ", error: " << err << dendl;
+      return r;
+    }
+  } else {
+    derr << __func__ << " bfm_blocks_per_key not found in bdev meta" << dendl;
+    return r;
+  }
+  r = 0;
+  return 0;
+}
+
 void BitmapFreelistManager::_init_misc()
 {
   bufferptr z(blocks_per_key >> 3);
@@ -224,6 +321,31 @@ void BitmapFreelistManager::_init_misc()
           << dendl;
 }
 
+void BitmapFreelistManager::sync(KeyValueDB* kvdb)
+{
+  _sync(kvdb, true);
+}
+
+void BitmapFreelistManager::_sync(KeyValueDB* kvdb, bool read_only)
+{
+  dout(10) << __func__ << " checks if size sync is needed" << dendl;
+  uint64_t size_db = 0;
+  int r = read_size_meta_from_db(kvdb, &size_db);
+  ceph_assert(r >= 0);
+  if (!read_only && size_db < size) {
+    dout(1) << __func__ << " committing new size 0x" << std::hex << size
+      << std::dec << dendl;
+    r = _expand(size_db, kvdb);
+    ceph_assert(r == 0);
+  } else if (size_db > size) {
+    // this might hapen when OSD passed the following sequence:
+    // upgrade -> downgrade -> expand -> upgrade
+    // One needs to run expand once again to syncup
+    dout(1) << __func__ << " fall back to legacy meta repo" << dendl;
+    _load_from_db(kvdb);
+  }
+}
+
 void BitmapFreelistManager::shutdown()
 {
   dout(1) << __func__ << dendl;
@@ -605,3 +727,30 @@ void BitmapFreelistManager::_xor(
     }
   }
 }
+
+uint64_t BitmapFreelistManager::size_2_block_count(uint64_t target_size) const
+{
+  auto target_blocks = target_size / bytes_per_block;
+  if (target_blocks / blocks_per_key * blocks_per_key != target_blocks) {
+    target_blocks = (target_blocks / blocks_per_key + 1) * blocks_per_key;
+  }
+  return target_blocks;
+}
+
+void BitmapFreelistManager::get_meta(
+  uint64_t target_size,
+  std::vector<std::pair<string, string>>* res) const
+{
+  if (target_size == 0) {
+    res->emplace_back("bfm_blocks", stringify(blocks));
+    res->emplace_back("bfm_size", stringify(size));
+  } else {
+    target_size = p2align(target_size, bytes_per_block);
+    auto target_blocks = size_2_block_count(target_size);
+
+    res->emplace_back("bfm_blocks", stringify(target_blocks));
+    res->emplace_back("bfm_size", stringify(target_size));
+  }
+  res->emplace_back("bfm_bytes_per_block", stringify(bytes_per_block));
+  res->emplace_back("bfm_blocks_per_key", stringify(blocks_per_key));
+}
index 97bee6ca9e178fc6d065bff129e70448be1c749a..09465da611b17b331931f7e3a51fe88e105f1a0d 100644 (file)
@@ -46,6 +46,17 @@ class BitmapFreelistManager : public FreelistManager {
     uint64_t offset, uint64_t length,
     KeyValueDB::Transaction txn);
 
+  int _init_from_label(const bluestore_bdev_label_t& label);
+
+  int _expand(uint64_t new_size, KeyValueDB* db);
+
+  uint64_t size_2_block_count(uint64_t target_size) const;
+
+  int read_size_meta_from_db(KeyValueDB* kvdb, uint64_t* res);
+  void _sync(KeyValueDB* kvdb, bool read_only);
+
+  void _load_from_db(KeyValueDB* kvdb);
+
 public:
   BitmapFreelistManager(CephContext* cct, std::string meta_prefix,
                        std::string bitmap_prefix);
@@ -55,11 +66,12 @@ public:
   int create(uint64_t size, uint64_t granularity,
             KeyValueDB::Transaction txn) override;
 
-  int expand(uint64_t new_size,
-             KeyValueDB::Transaction txn) override;
+  int init(const bluestore_bdev_label_t& l,
+    KeyValueDB *kvdb,
+    bool db_in_read_only) override;
 
-  int init(KeyValueDB *kvdb) override;
   void shutdown() override;
+  void sync(KeyValueDB* kvdb) override;
 
   void dump(KeyValueDB *kvdb) override;
 
@@ -82,7 +94,8 @@ public:
   inline uint64_t get_alloc_size() const override {
     return bytes_per_block;
   }
-
+  void get_meta(uint64_t target_size,
+    std::vector<std::pair<string, string>>*) const override;
 };
 
 #endif
index 995a39a9eabdc4875fb816cee439da84506a115f..57c89af4415d17832fcf60c52e348b88984e1e34 100644 (file)
@@ -4859,8 +4859,7 @@ int BlueStore::_check_or_set_bdev_label(
     if (cct->_conf->bluestore_debug_permit_any_bdev_label) {
       dout(20) << __func__ << " bdev " << path << " fsid " << label.osd_uuid
           << " and fsid " << fsid << " check bypassed" << dendl;
-    }
-    else if (label.osd_uuid != fsid) {
+    } else if (label.osd_uuid != fsid) {
       derr << __func__ << " bdev " << path << " fsid " << label.osd_uuid
           << " does not match our fsid " << fsid << dendl;
       return -EIO;
@@ -4969,8 +4968,11 @@ void BlueStore::_close_bdev()
   bdev = NULL;
 }
 
-int BlueStore::_open_fm(KeyValueDB::Transaction t)
+int BlueStore::_open_fm(KeyValueDB::Transaction t, bool read_only)
 {
+  int r;
+  bluestore_bdev_label_t label;
+
   ceph_assert(fm == NULL);
   fm = FreelistManager::create(cct, freelist_type, PREFIX_ALLOC);
   ceph_assert(fm);
@@ -5042,9 +5044,19 @@ int BlueStore::_open_fm(KeyValueDB::Transaction t)
        start += l + u;
       }
     }
+    r = _write_out_fm_meta(0, false, &label);
+    ceph_assert(r == 0);
+  } else {
+    string p = path + "/block";
+    r = _read_bdev_label(cct, p, &label);
+    if (r < 0) {
+      derr << __func__ << " freelist init failed, error reading bdev label: " << cpp_strerror(r) << dendl;
+      delete fm;
+      fm = NULL;
+      return r;
+    }
   }
-
-  int r = fm->init(db);
+  r = fm->init(label, db, read_only);
   if (r < 0) {
     derr << __func__ << " freelist init failed: " << cpp_strerror(r) << dendl;
     delete fm;
@@ -5076,6 +5088,34 @@ void BlueStore::_close_fm()
   fm = NULL;
 }
 
+int BlueStore::_write_out_fm_meta(uint64_t target_size,
+  bool update_root_size,
+  bluestore_bdev_label_t* res_label)
+{
+  string p = path + "/block";
+
+  std::vector<std::pair<string, string>> fm_meta;
+  fm->get_meta(target_size, &fm_meta);
+
+  bluestore_bdev_label_t label;
+  int r = _read_bdev_label(cct, p, &label);
+  if (r < 0)
+    return r;
+
+  for (auto& m : fm_meta) {
+    label.meta[m.first] = m.second;
+  }
+  if (update_root_size) {
+    label.size = target_size;
+  }
+  r = _write_bdev_label(cct, p, label);
+  if (res_label) {
+    *res_label = label;
+  }
+
+  return r;
+}
+
 int BlueStore::_open_alloc()
 {
   ceph_assert(alloc == NULL);
@@ -5538,7 +5578,7 @@ int BlueStore::_open_db_and_around(bool read_only)
       goto out_db;
     }
 
-    r = _open_fm(nullptr);
+    r = _open_fm(nullptr, true);
     if (r < 0)
       goto out_db;
 
@@ -5556,6 +5596,7 @@ int BlueStore::_open_db_and_around(bool read_only)
        _close_fm();
        return r;
       }
+      fm->sync(db);
     }
   } else {
     r = _open_db(false, false);
@@ -5567,7 +5608,7 @@ int BlueStore::_open_db_and_around(bool read_only)
       goto out_db;
     }
 
-    r = _open_fm(nullptr);
+    r = _open_fm(nullptr, false);
     if (r < 0)
       goto out_db;
 
@@ -6421,7 +6462,7 @@ int BlueStore::mkfs()
 
   {
     KeyValueDB::Transaction t = db->get_transaction();
-    r = _open_fm(t);
+    r = _open_fm(t, true);
     if (r < 0)
       goto out_close_db;
     {
@@ -6868,43 +6909,19 @@ int BlueStore::expand_devices(ostream& out)
   }
   uint64_t size0 = fm->get_size();
   uint64_t size = bdev->get_size();
-  cold_close();
   if (size0 < size) {
-    out << "Expanding Main..." << std::endl;
-    int r = _mount(false);
-    ceph_assert(r == 0);
-
     out << bluefs_layout.shared_bdev
-       <<" : expanding " << " from 0x" << std::hex
-       << size0 << " to 0x" << size << std::dec << std::endl;
-    KeyValueDB::Transaction txn;
-    txn = db->get_transaction();
-    r = fm->expand(size, txn);
+      << " : expanding " << " from 0x" << std::hex
+      << size0 << " to 0x" << size << std::dec << std::endl;
+    _write_out_fm_meta(size, true);
+    cold_close();
+
+    // mount in read/write to sync expansion changes
+    r = _mount(false);
     ceph_assert(r == 0);
-    db->submit_transaction_sync(txn);
-
-     // always reference to slow device here
-    string p = get_device_path(BlueFS::BDEV_SLOW);
-    ceph_assert(!p.empty());
-    const char* path = p.c_str();
-    bluestore_bdev_label_t label;
-    r = _read_bdev_label(cct, path, &label);
-    if (r < 0) {
-      derr << "unable to read label for " << path << ": "
-           << cpp_strerror(r) << dendl;
-    } else {
-      label.size = size;
-      r = _write_bdev_label(cct, path, label);
-      if (r < 0) {
-       derr << "unable to write label for " << path << ": "
-             << cpp_strerror(r) << dendl;
-      } else {
-       out << bluefs_layout.shared_bdev
-             <<" : size label updated to " << size
-             << std::endl;
-      }
-    }
     umount();
+  } else {
+    cold_close();
   }
   return r;
 }
@@ -11081,6 +11098,14 @@ int BlueStore::_upgrade_super()
       int r = db->submit_transaction_sync(t);
       ceph_assert(r == 0);
     }
+    if (ondisk_format == 3) {
+      // changes:
+      // - FreelistManager keeps meta within bdev label
+      int r = _write_out_fm_meta(0);
+      ceph_assert(r == 0);
+
+      ondisk_format = 4;
+    }
   }
   // done
   dout(1) << __func__ << " done" << dendl;
index e95fafa5d9548c590f49493f45081997d8809c89..e8cd2eaa043bc9172573164b661ad4fdd2466448 100644 (file)
@@ -2271,8 +2271,11 @@ private:
               bool to_repair_db=false,
               bool read_only = false);
   void _close_db(bool read_only);
-  int _open_fm(KeyValueDB::Transaction t);
+  int _open_fm(KeyValueDB::Transaction t, bool read_only);
   void _close_fm();
+  int _write_out_fm_meta(uint64_t target_size,
+    bool update_root_size = false,
+    bluestore_bdev_label_t* res_label = nullptr);
   int _open_alloc();
   void _close_alloc();
   int _open_collections();
@@ -2431,7 +2434,7 @@ private:
 
   // -- ondisk version ---
 public:
-  const int32_t latest_ondisk_format = 3;        ///< our version
+  const int32_t latest_ondisk_format = 4;        ///< our version
   const int32_t min_readable_ondisk_format = 1;  ///< what we can read
   const int32_t min_compat_ondisk_format = 3;    ///< who can read us
 
index 58a4c617798346d82991802e6791714acb7a9928..d1c4976f58cfac43180cdae31def6b64f1e0cec2 100644 (file)
@@ -5,10 +5,11 @@
 #define CEPH_OS_BLUESTORE_FREELISTMANAGER_H
 
 #include <string>
-#include <map>
+#include <vector>
 #include <mutex>
 #include <ostream>
 #include "kv/KeyValueDB.h"
+#include "bluestore_types.h"
 
 class FreelistManager {
 public:
@@ -26,10 +27,10 @@ public:
   virtual int create(uint64_t size, uint64_t granularity,
                     KeyValueDB::Transaction txn) = 0;
 
-  virtual int expand(uint64_t new_size,
-                    KeyValueDB::Transaction txn) = 0;
-
-  virtual int init(KeyValueDB *kvdb) = 0;
+  virtual int init(const bluestore_bdev_label_t& l,
+    KeyValueDB *kvdb,
+    bool db_in_read_only) = 0;
+  virtual void sync(KeyValueDB* kvdb) = 0;
   virtual void shutdown() = 0;
 
   virtual void dump(KeyValueDB *kvdb) = 0;
@@ -48,6 +49,8 @@ public:
   virtual uint64_t get_alloc_units() const = 0;
   virtual uint64_t get_alloc_size() const = 0;
 
+  virtual void get_meta(uint64_t target_size,
+    std::vector<std::pair<string, string>>*) const = 0;
 };