]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
os/bluestore/BlueFS: many fixes
authorSage Weil <sage@redhat.com>
Thu, 10 Dec 2015 21:30:47 +0000 (16:30 -0500)
committerSage Weil <sage@redhat.com>
Fri, 1 Jan 2016 18:06:54 +0000 (13:06 -0500)
Signed-off-by: Sage Weil <sage@redhat.com>
src/common/config_opts.h
src/os/bluestore/BlueFS.cc
src/os/bluestore/BlueFS.h
src/os/bluestore/BlueRocksEnv.cc
src/os/bluestore/BlueRocksEnv.h
src/os/bluestore/BlueStore.cc
src/os/bluestore/BlueStore.h
src/os/bluestore/MirrorEnv.h [new file with mode: 0644]

index 763b06108d5a7796f3af2c913e4170b0972deddb..04dc79aadcaee0d3a8f4fa3791acf37f14803551 100644 (file)
@@ -838,6 +838,7 @@ OPTION(bluefs_min_log_runway, OPT_U64, 1048576)  // alloc when we get this low
 OPTION(bluefs_max_log_runway, OPT_U64, 4194304)  // alloc this much at a time
 
 OPTION(bluestore_bluefs, OPT_BOOL, false)
+OPTION(bluestore_bluefs_mirror, OPT_BOOL, false) // mirror to normal Env for debug
 OPTION(bluestore_bluefs_initial_offset, OPT_U64,  1024*1024)
 OPTION(bluestore_bluefs_initial_length, OPT_U64, 65536*1024)
 OPTION(bluestore_bluefs_min_ratio, OPT_FLOAT, .01)
index 62480eb8d18f62ae5ee54d640d21ca3f17bf03ab..2a0d822ba6e4557c5704b1ee61c8032863a25344 100644 (file)
@@ -574,7 +574,8 @@ int BlueFS::_read(
   char *out)      ///< [out] optional: or copy it here
 {
   Mutex::Locker l(h->lock);
-  dout(10) << __func__ << " h " << h << " len " << len << dendl;
+  dout(10) << __func__ << " h " << h << " " << off << "~" << len
+          << " from " << h->file->fnode << dendl;
   if (!h->ignore_eof &&
       off + len > h->file->fnode.size) {
     len = h->file->fnode.size - off;
@@ -589,7 +590,10 @@ int BlueFS::_read(
     h->bl_off = off & super.block_mask();
     uint64_t x_off = 0;
     vector<bluefs_extent_t>::iterator p = h->file->fnode.seek(h->bl_off, &x_off);
-    uint64_t l = MIN(p->length - x_off, h->max_prefetch);
+    uint64_t want = ROUND_UP_TO(len + (off & ~super.block_mask()),
+                               super.block_size);
+    want = MAX(want, h->max_prefetch);
+    uint64_t l = MIN(p->length - x_off, want);
     uint64_t eof_offset = ROUND_UP_TO(h->file->fnode.size, super.block_size);
     if (!h->ignore_eof &&
        h->bl_off + l > eof_offset) {
@@ -600,7 +604,8 @@ int BlueFS::_read(
     int r = bdev[p->bdev]->read(p->offset + x_off, l, &h->bl, ioc[p->bdev]);
     assert(r == 0);
   }
-  left = h->get_buf_remaining();
+  left = h->get_buf_remaining(off);
+  dout(20) << __func__ << " left " << left << dendl;
 
   int r = MIN(len, left);
   // NOTE: h->bl is normally a contiguous buffer so c_str() is free.
@@ -608,6 +613,13 @@ int BlueFS::_read(
     *bp = bufferptr(h->bl.c_str() + off - h->bl_off, r);
   if (out)
     memcpy(out, h->bl.c_str() + off - h->bl_off, r);
+
+  dout(30) << __func__ << " result (" << r << " bytes):\n";
+  bufferlist t;
+  t.substr_of(h->bl, off - h->bl_off, r);
+  t.hexdump(*_dout);
+  *_dout << dendl;
+
   h->pos = off + r;
   dout(20) << __func__ << " got " << r << dendl;
   return r;
@@ -732,6 +744,10 @@ int BlueFS::_flush_range(FileWriter *h, uint64_t offset, uint64_t length)
   }
   assert(bl.length() == length);
 
+  dout(30) << "dump:\n";
+  bl.hexdump(*_dout);
+  *_dout << dendl;
+
   h->pos = offset + length;
   h->tail_block.clear();
 
@@ -1073,9 +1089,10 @@ int BlueFS::rmdir(const string& dirname)
 bool BlueFS::dir_exists(const string& dirname)
 {
   Mutex::Locker l(lock);
-  dout(10) << __func__ << " " << dirname << dendl;
   map<string,Dir*>::iterator p = dir_map.find(dirname);
-  return p != dir_map.end();
+  bool exists = p != dir_map.end();
+  dout(10) << __func__ << " " << dirname << " = " << (int)exists << dendl;
+  return exists;
 }
 
 int BlueFS::stat(const string& dirname, const string& filename,
@@ -1163,10 +1180,12 @@ int BlueFS::readdir(const string& dirname, vector<string> *ls)
     return -ENOENT;
   }
   Dir *dir = p->second;
-  ls->reserve(dir->file_map.size());
+  ls->reserve(dir->file_map.size() + 2);
   for (auto q : dir->file_map) {
     ls->push_back(q.first);
   }
+  ls->push_back(".");
+  ls->push_back("..");
   return 0;
 }
 
index e79ec7a1bb5e3840c9edf73af0c50a7c90c6cc6f..853c784dc41d9f5b5c8642ea1a33c3e4af928f1a 100644 (file)
@@ -79,9 +79,9 @@ public:
     uint64_t get_buf_end() {
       return bl_off + bl.length();
     }
-    uint64_t get_buf_remaining() {
-      if (bl_off + bl.length() > pos)
-       return bl_off + bl.length() - pos;
+    uint64_t get_buf_remaining(uint64_t p) {
+      if (p >= bl_off && p < bl_off + bl.length())
+       return bl_off + bl.length() - p;
       return 0;
     }
 
index b52b2309504a1a365e45c00709c58850694ca39d..49f70db12e48e36694c33cd8a9b7cb1e21953915 100644 (file)
@@ -315,6 +315,8 @@ rocksdb::Status BlueRocksEnv::NewSequentialFile(
   std::unique_ptr<rocksdb::SequentialFile>* result,
   const rocksdb::EnvOptions& options)
 {
+  if (fname[0] == '/')
+    return target()->NewSequentialFile(fname, result, options);
   std::string dir, file;
   split(fname, &dir, &file);
   BlueFS::FileReader *h;
@@ -382,15 +384,16 @@ rocksdb::Status BlueRocksEnv::NewDirectory(
   const std::string& name,
   std::unique_ptr<rocksdb::Directory>* result)
 {
-  int r = fs->dir_exists(name);
-  if (r < 0)
-    return err_to_status(r);
+  if (!fs->dir_exists(name))
+    return rocksdb::Status::IOError(name, strerror(-ENOENT));
   result->reset(new BlueRocksDirectory(fs));
   return rocksdb::Status::OK();
 }
 
 rocksdb::Status BlueRocksEnv::FileExists(const std::string& fname)
 {
+  if (fname[0] == '/')
+    return target()->FileExists(fname);
   std::string dir, file;
   split(fname, &dir, &file);
   if (fs->stat(dir, file, NULL, NULL) == 0)
@@ -404,7 +407,7 @@ rocksdb::Status BlueRocksEnv::GetChildren(
 {
   int r = fs->readdir(dir, result);
   if (r < 0)
-    return err_to_status(r);
+    return rocksdb::Status::IOError(dir, strerror(-ENOENT));//    return err_to_status(r);
   return rocksdb::Status::OK();
 }
 
index d8020109bec5235027aad53d9872a3e538bdc306..82484d73939ac3f19da2a661510d400605e925d8 100644 (file)
@@ -16,8 +16,10 @@ class BlueFS;
 class BlueRocksEnv : public rocksdb::EnvWrapper {
   void split(const std::string &fn, std::string *dir, std::string *file) {
     size_t slash = fn.rfind('/');
-    *dir = fn.substr(0, slash);
     *file = fn.substr(slash + 1);
+    while (slash && fn[slash-1] == '/')
+      --slash;
+    *dir = fn.substr(0, slash);
   }
 
 public:
index c4032f4c8e3ff03feae211554019df0c8edf198d..0d9249d161c2d2e629f397c6a9ab98e50d6ce706 100644 (file)
@@ -29,6 +29,7 @@
 #include "FreelistManager.h"
 #include "BlueFS.h"
 #include "BlueRocksEnv.h"
+#include "MirrorEnv.h"
 
 
 #define dout_subsys ceph_subsys_bluestore
@@ -922,7 +923,7 @@ int BlueStore::_open_db(bool create)
   char fn[PATH_MAX];
   snprintf(fn, sizeof(fn), "%s/db", path.c_str());
 
-  void *env = NULL;
+  rocksdb::Env *env = NULL;
   if (g_conf->bluestore_bluefs) {
     dout(10) << __func__ << " initializing bluefs" << dendl;
     if (g_conf->bluestore_backend != "rocksdb") {
@@ -944,11 +945,25 @@ int BlueStore::_open_db(bool create)
       delete bluefs;
       return r;
     }
-    bluerocksenv = new BlueRocksEnv(bluefs);
-    env = static_cast<void*>(bluerocksenv);
+    if (g_conf->bluestore_bluefs_mirror) {
+      rocksdb::Env *a = new BlueRocksEnv(bluefs);
+      unique_ptr<rocksdb::Directory> dir;
+      rocksdb::Env *b = rocksdb::Env::Default();
+      if (create) {
+       string cmd = "rm -r " + path + "/db";
+       system(cmd.c_str());
+      }
+      env = new rocksdb::MirrorEnv(b, a);
+    } else {
+      env = new BlueRocksEnv(bluefs);
+    }
+
+    if (create) {
+      env->CreateDir(fn);
+    }
 
     // simplify the dir names, too, as "seen" by rocksdb
-    strcpy(fn, "db");
+    //strcpy(fn, "bluefs/db");
   } else if (create) {
     int r = ::mkdir(fn, 0755);
     if (r < 0)
@@ -976,10 +991,9 @@ int BlueStore::_open_db(bool create)
   db = KeyValueDB::create(g_ceph_context,
                          g_conf->bluestore_backend,
                          fn,
-                         env);
+                         static_cast<void*>(env));
   if (!db) {
     derr << __func__ << " error creating db" << dendl;
-    delete bluerocksenv;
     bluefs->umount();
     delete bluefs;
     delete db;
@@ -998,7 +1012,6 @@ int BlueStore::_open_db(bool create)
     r = db->open(err);
   if (r) {
     derr << __func__ << " erroring opening db: " << err.str() << dendl;
-    delete bluerocksenv;
     bluefs->umount();
     delete bluefs;
     delete db;
@@ -1030,8 +1043,6 @@ void BlueStore::_close_db()
   delete db;
   db = NULL;
   if (bluefs) {
-    delete bluerocksenv;
-    bluerocksenv = NULL;
     bluefs->umount();
     delete bluefs;
     bluefs = NULL;
index cccac5f38ddadb05e43d33efc38e45b6e8141075..0c7bc11fd7ca18712d6a2f82cb8652c830cdd739 100644 (file)
@@ -37,7 +37,6 @@
 class Allocator;
 class FreelistManager;
 class BlueFS;
-class BlueRocksEnv;
 
 class BlueStore : public ObjectStore {
   // -----------------------------------------------------
@@ -392,7 +391,6 @@ public:
 private:
   CephContext *cct;
   BlueFS *bluefs;
-  BlueRocksEnv *bluerocksenv;
   KeyValueDB *db;
   FS *fs;
   BlockDevice *bdev;
diff --git a/src/os/bluestore/MirrorEnv.h b/src/os/bluestore/MirrorEnv.h
new file mode 100644 (file)
index 0000000..2a0c2c6
--- /dev/null
@@ -0,0 +1,348 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#ifndef CEPH_OS_BLUESTORE_MIRRORENV_H
+#define CEPH_OS_BLUESTORE_MIRRORENV_H
+
+#include <memory>
+#include <string>
+
+#include "rocksdb/status.h"
+#include "rocksdb/env.h"
+
+namespace rocksdb {
+
+  class MirrorSequentialFile : public SequentialFile {
+  public:
+    unique_ptr<SequentialFile> a_, b_;
+    string fname;
+    MirrorSequentialFile(string f) : fname(f) {}
+
+    Status Read(size_t n, Slice* result, char* scratch) {
+      Slice aslice;
+      Status as = a_->Read(n, &aslice, scratch);
+      if (as == Status::OK()) {
+       char bscratch[n];
+       Slice bslice;
+       unsigned off = 0, left = result->size();
+       while (left) {
+         Status bs = b_->Read(left, &bslice, bscratch);
+         assert(as == bs);
+         assert(memcmp(bscratch, scratch + off, bslice.size()) == 0);
+         off += bslice.size();
+         left -= bslice.size();
+       }
+       *result = aslice;
+      } else {
+       Status bs = b_->Read(n, result, scratch);
+       assert(as == bs);
+      }
+      return as;
+    }
+
+    Status Skip(uint64_t n) {
+      Status as = a_->Skip(n);
+      Status bs = b_->Skip(n);
+      assert(as == bs);
+      return as;
+    }
+    Status InvalidateCache(size_t offset, size_t length) {
+      Status as = a_->InvalidateCache(offset, length);
+      Status bs = b_->InvalidateCache(offset, length);
+      assert(as == bs);
+      return as;
+    };
+  };
+
+  class MirrorRandomAccessFile : public RandomAccessFile {
+  public:
+    unique_ptr<RandomAccessFile> a_, b_;
+    string fname;
+    MirrorRandomAccessFile(string f) : fname(f) {}
+
+    Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const {
+      Status as = a_->Read(offset, n, result, scratch);
+      if (as == Status::OK()) {
+       char bscratch[n];
+       Slice bslice;
+       unsigned off = 0, left = result->size();
+       while (left) {
+         Status bs = b_->Read(offset + off, left, &bslice, bscratch);
+         assert(as == bs);
+         assert(memcmp(bscratch, scratch + off, bslice.size()) == 0);
+         off += bslice.size();
+         left -= bslice.size();
+       }
+      } else {
+       Status bs = b_->Read(offset, n, result, scratch);
+       assert(as == bs);
+      }
+      return as;
+    }
+
+    bool ShouldForwardRawRequest() const {
+      // FIXME: not verified
+      return a_->ShouldForwardRawRequest();
+    }
+
+    size_t GetUniqueId(char* id, size_t max_size) const {
+      // FIXME: not verified
+      return a_->GetUniqueId(id, max_size);
+    }
+  };
+
+  class MirrorWritableFile : public WritableFile {
+  public:
+    unique_ptr<WritableFile> a_, b_;
+    string fname;
+    MirrorWritableFile(string f) : fname(f) {}
+
+    Status Append(const Slice& data) override {
+      Status as = a_->Append(data);
+      Status bs = b_->Append(data);
+      assert(as == bs);
+      return as;
+    }
+    Status PositionedAppend(const Slice& data, uint64_t offset) override {
+      Status as = a_->PositionedAppend(data, offset);
+      Status bs = b_->PositionedAppend(data, offset);
+      assert(as == bs);
+      return as;
+    }
+    Status Truncate(uint64_t size) override {
+      Status as = a_->Truncate(size);
+      Status bs = b_->Truncate(size);
+      assert(as == bs);
+      return as;
+    }
+    Status Close() override {
+      Status as = a_->Close();
+      Status bs = b_->Close();
+      assert(as == bs);
+      return as;
+    }
+    Status Flush() override {
+      Status as = a_->Flush();
+      Status bs = b_->Flush();
+      assert(as == bs);
+      return as;
+    }
+    Status Sync() override {
+      Status as = a_->Sync();
+      Status bs = b_->Sync();
+      assert(as == bs);
+      return as;
+    }
+    Status Fsync() override {
+      Status as = a_->Fsync();
+      Status bs = b_->Fsync();
+      assert(as == bs);
+      return as;
+    }
+    bool IsSyncThreadSafe() const override {
+      bool as = a_->IsSyncThreadSafe();
+      bool bs = b_->IsSyncThreadSafe();
+      assert(as == bs);
+      return as;
+    }
+    void SetIOPriority(Env::IOPriority pri) override {
+      a_->SetIOPriority(pri);
+      b_->SetIOPriority(pri);
+    }
+    Env::IOPriority GetIOPriority() override {
+      // FIXME: we don't verify this one
+      return a_->GetIOPriority();
+    }
+    uint64_t GetFileSize() override {
+      uint64_t as = a_->GetFileSize();
+      uint64_t bs = b_->GetFileSize();
+      assert(as == bs);
+      return as;
+    }
+    void GetPreallocationStatus(size_t* block_size,
+                               size_t* last_allocated_block) override {
+      // FIXME: we don't verify this one
+      return a_->GetPreallocationStatus(block_size, last_allocated_block);
+    }
+    size_t GetUniqueId(char* id, size_t max_size) const override {
+      // FIXME: we don't verify this one
+      return a_->GetUniqueId(id, max_size);
+    }
+    Status InvalidateCache(size_t offset, size_t length) override {
+      Status as = a_->InvalidateCache(offset, length);
+      Status bs = b_->InvalidateCache(offset, length);
+      assert(as == bs);
+      return as;
+    }
+
+  protected:
+    Status Allocate(off_t offset, off_t length) override {
+      Status as = a_->Allocate(offset, length);
+      Status bs = b_->Allocate(offset, length);
+      assert(as == bs);
+      return as;
+    }
+    Status RangeSync(off_t offset, off_t nbytes) override {
+      Status as = a_->RangeSync(offset, nbytes);
+      Status bs = b_->RangeSync(offset, nbytes);
+      assert(as == bs);
+      return as;
+    }
+  };
+
+  class MirrorEnv : public EnvWrapper {
+    Env *a_, *b_;
+  public:
+    MirrorEnv(Env *a, Env *b) : EnvWrapper(a), a_(a), b_(b) {}
+
+    // The following text is boilerplate that forwards all methods to target()
+    Status NewSequentialFile(const std::string& f, unique_ptr<SequentialFile>* r,
+                            const EnvOptions& options) override {
+      if (f[0] == '/')
+       return a_->NewSequentialFile(f, r, options);
+      MirrorSequentialFile *mf = new MirrorSequentialFile(f);
+      r->reset(mf);
+      Status as = a_->NewSequentialFile(f, &mf->a_, options);
+      Status bs = b_->NewSequentialFile(f, &mf->b_, options);
+      assert(as == bs);
+      return as;
+    }
+    Status NewRandomAccessFile(const std::string& f,
+                              unique_ptr<RandomAccessFile>* r,
+                              const EnvOptions& options) override {
+      if (f[0] == '/')
+       return a_->NewRandomAccessFile(f, r, options);
+      MirrorRandomAccessFile *mf = new MirrorRandomAccessFile(f);
+      r->reset(mf);
+      Status as = a_->NewRandomAccessFile(f, &mf->a_, options);
+      Status bs = b_->NewRandomAccessFile(f, &mf->b_, options);
+      assert(as == bs);
+      return as;
+    }
+    Status NewWritableFile(const std::string& f, unique_ptr<WritableFile>* r,
+                          const EnvOptions& options) override {
+      if (f[0] == '/')
+       return a_->NewWritableFile(f, r, options);
+      MirrorWritableFile *mf = new MirrorWritableFile(f);
+      r->reset(mf);
+      Status as = a_->NewWritableFile(f, &mf->a_, options);
+      Status bs = b_->NewWritableFile(f, &mf->b_, options);
+      assert(as == bs);
+      return as;
+    }
+    virtual Status NewDirectory(const std::string& name,
+                               unique_ptr<Directory>* result) override {
+      unique_ptr<Directory> br;
+      Status as = a_->NewDirectory(name, result);
+      Status bs = b_->NewDirectory(name, &br);
+      assert(as == bs);
+      return as;
+    }
+    Status FileExists(const std::string& f) override {
+      Status as = a_->FileExists(f);
+      Status bs = b_->FileExists(f);
+      assert(as == bs);
+      return as;
+    }
+    Status GetChildren(const std::string& dir,
+                      std::vector<std::string>* r) override {
+      std::vector<std::string> ar, br;
+      Status as = a_->GetChildren(dir, &ar);
+      Status bs = b_->GetChildren(dir, &br);
+      assert(as == bs);
+      std::sort(ar.begin(), ar.end());
+      std::sort(br.begin(), br.end());
+      if (ar != br) {
+       std::cout << "a: " << ar << std::endl;
+       std::cout << "b: " << br << std::endl;
+       assert(0 == "getchildren results don't match");
+      }
+      *r = ar;
+      return as;
+    }
+    Status DeleteFile(const std::string& f) override {
+      Status as = a_->DeleteFile(f);
+      Status bs = b_->DeleteFile(f);
+      assert(as == bs);
+      return as;
+    }
+    Status CreateDir(const std::string& d) override {
+      Status as = a_->CreateDir(d);
+      Status bs = b_->CreateDir(d);
+      assert(as == bs);
+      return as;
+    }
+    Status CreateDirIfMissing(const std::string& d) override {
+      Status as = a_->CreateDirIfMissing(d);
+      Status bs = b_->CreateDirIfMissing(d);
+      assert(as == bs);
+      return as;
+    }
+    Status DeleteDir(const std::string& d) override {
+      Status as = a_->DeleteDir(d);
+      Status bs = b_->DeleteDir(d);
+      assert(as == bs);
+      return as;
+    }
+    Status GetFileSize(const std::string& f, uint64_t* s) override {
+      uint64_t asize, bsize;
+      Status as = a_->GetFileSize(f, &asize);
+      Status bs = b_->GetFileSize(f, &bsize);
+      assert(as == bs);
+      assert(asize == bsize);
+      *s = asize;
+      return as;
+    }
+
+    Status GetFileModificationTime(const std::string& fname,
+                                  uint64_t* file_mtime) override {
+      uint64_t amtime, bmtime;
+      Status as = a_->GetFileModificationTime(fname, &amtime);
+      Status bs = b_->GetFileModificationTime(fname, &bmtime);
+      assert(as == bs);
+      assert(amtime - bmtime < 10000 || bmtime - amtime < 10000);
+      *file_mtime = amtime;
+      return as;
+    }
+
+    Status RenameFile(const std::string& s, const std::string& t) override {
+      Status as = a_->RenameFile(s, t);
+      Status bs = b_->RenameFile(s, t);
+      assert(as == bs);
+      return as;
+    }
+
+    Status LinkFile(const std::string& s, const std::string& t) override {
+      Status as = a_->LinkFile(s, t);
+      Status bs = b_->LinkFile(s, t);
+      assert(as == bs);
+      return as;
+    }
+
+    class MirrorFileLock : public FileLock {
+    public:
+      FileLock *a_, *b_;
+      MirrorFileLock(FileLock *a, FileLock *b) : a_(a), b_(b) {}
+    };
+
+    Status LockFile(const std::string& f, FileLock** l) override {
+      FileLock *al, *bl;
+      Status as = a_->LockFile(f, &al);
+      Status bs = b_->LockFile(f, &bl);
+      *l = new MirrorFileLock(al, bl);
+      assert(as == bs);
+      return as;
+    }
+
+    Status UnlockFile(FileLock* l) override {
+      MirrorFileLock *ml = static_cast<MirrorFileLock*>(l);
+      Status as = a_->UnlockFile(ml->a_);
+      Status bs = b_->UnlockFile(ml->b_);
+      assert(as == bs);
+      return as;
+    }
+
+  };
+
+}
+
+#endif