文章目录
背景
在BlueStore存储引擎中,数据和元数据的存储不再经过操作系统层,OSD直接管理裸盘空间,通过libaio对裸盘进行读写。为了更好的兼容性,BlueStore提供了BlockDevice层作为写盘IO的调度层,也管理不同类型的盘,如HDD、Sata SSD和Nvme SSD。
本文后续基于N版本进行介绍,主要介绍HDD场景,HDD场景BlockDevice会抽象为KernelDevice类。
本文结论
- Device使用流程为
- 异步写:aio_write(异步写入队)、aio_submit(遍历队列写盘)、aio_thread(检查写盘完成)
- 同步写:write(同步写盘)
- 同步刷盘:flush(同步刷盘)
- 同步trim:discard(trim)
- 后台trim:queue_discard(trim extent入队)、discard_thread(执行队列中trim任务)
- Device设备文件主要有两种(block和block.db),需要在osd创建的时候将本地磁盘软链接到Device设备文件。
- block:用于存储数据。
- block.db:用于存储rocksdb的元数据。
- BlockDevice模块提供块设备文件的访问接口,空间的管理需要上层使用者控制。
- 异步写的过程是并发执行的,两个相互覆盖的IO,是无法保证顺序性的,需要使用者进行互斥的控制。
代码分析
数据结构
下面只解释部分关键字段
class BlockDevice {
uint64_t size; // 设备总大小
uint64_t block_size; // 块大小
}
class KernelDevice : public BlockDevice {
std::vector<int> fd_direct, fd_buffered; // 裸设备以direct、buffered两种方式打开的fd
std::string path; // 设备路径
bool aio, dio; // 是否启用Libaio
interval_set<uint64_t> discard_queued; // interval_set是offset+length, discard_queued 存放需要做Discard的Extent。
interval_set<uint64_t> discard_finishing; // discard_finishing 和 discard_queued 交换值,存放完成Discard的Extent
// Libaio线程,收割完成的事件
struct AioCompletionThread : public Thread {
KernelDevice *bdev;
explicit AioCompletionThread(KernelDevice *b) : bdev(b) {
}
void *entry() override {
bdev->_aio_thread();
return NULL;
}
} aio_thread;
// Discard线程,用于SSD的Trim
struct DiscardThread : public Thread {
KernelDevice *bdev;
explicit DiscardThread(KernelDevice *b) : bdev(b) {
}
void *entry() override {
bdev->_discard_thread();
return NULL;
}
} discard_thread;
// 同步IO
int read(uint64_t off, uint64_t len, bufferlist *pbl, IOContext *ioc,
bool buffered) override;
int write(uint64_t off, bufferlist &bl, bool buffered) override;
// 异步IO
int aio_read(uint64_t off, uint64_t len, bufferlist *pbl,
IOContext *ioc) override;
int aio_read(uint64_t off, uint64_t len, bufferlist *pbl,
IOContext *ioc) override;
void aio_submit(IOContext *ioc) override;
// sync数据
int flush() override;
// 对SSD指定offset、len的数据做Trim
int discard(uint64_t offset, uint64_t len) override;
};
打开device
create
BlueFS用户态文件系统会使用BlockDevice存放文件系统的数据,BlueStore也会使用BlockDevice存放object相关的数据,创建BlockDevice的时候,通过工厂函数create,根据不同的设备类型,创建不同的设备。
- 通过上层调用可以看到创建的块设备有两种,见BlueStore::_minimal_open_bluefs等函数。
- /pathname/block:用于存放数据。
- /pathname/block.db:用于存放rocksdb元数据。
- 设备初始化主要是一种内存的操作,真正的块设备是本地磁盘,存储池创建后可以使用软链接的方式将本地磁盘挂载到block或block.db文件,后续对其读写就是对盘的读写了。
BlockDevice *BlockDevice::create(CephContext* cct, const string& path,
aio_callback_t cb, void *cbpriv) {
// 通过设备的path,判断出设备的类型
string type = "kernel";
char buf[PATH_MAX + 1];
int r = ::readlink(path.c_str(), buf, sizeof(buf) - 1);
if (r >= 0) {
buf[r