]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.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>
Fri, 17 Apr 2020 10:34:17 +0000 (13:34 +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>
(cherry picked from commit 69a43efccfd3289a3ffec1d76dc4b4a208e0ec0c)

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 7dcef4384adb1058f8832581b8e08eb69fe4ec57..adf85e98a264def8a46bd0999943a526168ce2d9 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"
 
@@ -66,9 +67,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;
@@ -104,28 +104,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 = (blocks / 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 past-eof blocks to unallocated
-    _xor(size, blocks0 * bytes_per_block - size, txn);
+    _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);
@@ -147,13 +148,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());
 
@@ -165,31 +182,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
@@ -201,6 +231,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);
@@ -217,6 +314,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;
@@ -598,3 +720,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 9f076e77f0a923ee912d4fcf736d45f2746053d4..61d6ad441f76072ef9ea0769bbb74d8ded4f992a 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, string meta_prefix,
                        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 cf1524e3f4dcbcb85556eb74a87bcfd8725415b4..d6e7bf5fab15e3f274bf366b86f7990ad98ae61a 100644 (file)
@@ -4834,8 +4834,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;
@@ -4944,8 +4943,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);
@@ -5017,9 +5019,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;
@@ -5051,6 +5063,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);
@@ -5513,7 +5553,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;
 
@@ -5531,6 +5571,7 @@ int BlueStore::_open_db_and_around(bool read_only)
        _close_fm();
        return r;
       }
+      fm->sync(db);
     }
   } else {
     r = _open_db(false, false);
@@ -5542,7 +5583,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;
 
@@ -6396,7 +6437,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;
     {
@@ -6843,43 +6884,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;
 }
@@ -11056,6 +11073,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 e9b71ea0b93169f4a5e404b39b09f4dc9e1a3bf8..55787e1b2986bd61dcc129b3205ba38e690cb190 100644 (file)
@@ -2270,8 +2270,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();
@@ -2430,7 +2433,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 56e05d1434a01bc80460ef4855aeaa1172813d52..300112e13ef86c86cd1d2235df0838cad89a3664 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;
 };