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;
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) {
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.
*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;
}
assert(bl.length() == length);
+ dout(30) << "dump:\n";
+ bl.hexdump(*_dout);
+ *_dout << dendl;
+
h->pos = offset + length;
h->tail_block.clear();
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,
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;
}
--- /dev/null
+// -*- 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