SeaStore 硬盘存储调用机制
1. 概述
SeaStore 通过多层抽象来访问底层存储设备,最终使用 Seastar 框架的 DMA(Direct Memory Access)接口进行实际的 I/O 操作。本文档详细说明从应用层到硬件层的完整调用路径。
2. 调用层次结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 应用层 (SeaStore API) │ ▼ 事务管理层 (TransactionManager) │ ▼ 缓存层 (Cache) │ ▼ Extent放置管理层 (ExtentPlacementManager) │ ├─► 段式设备路径 │ │ │ ▼ │ SegmentManager (Block/ZBD) │ │ │ ▼ │ Segment::write() / Segment::read() │ │ │ ▼ │ seastar::file::dma_write() / dma_read() │ └─► 随机块设备路径 │ ▼ RandomBlockManager │ ▼ seastar::file::dma_write() / dma_read()
|
3. 设备抽象层
3.1 Device 接口
位置: device.h
Device 是存储设备的抽象接口,定义了统一的读写接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Device { public: virtual read_ertr::future<> read( paddr_t addr, size_t len, ceph::bufferptr &out ) = 0;
virtual device_type_t get_device_type() const = 0; virtual backend_type_t get_backend_type() const = 0; virtual extent_len_t get_block_size() const = 0; };
|
3.2 设备实现类型
SeaStore 支持两种主要的设备类型:
段式设备 (SEGMENTED):
BlockSegmentManager: 普通块设备
ZBDSegmentManager: Zoned Block Device (ZBD)
EphemeralSegmentManager: 临时内存设备(测试用)
随机块设备 (RANDOM_BLOCK):
RandomBlockManager: 随机访问块设备
4. 段式设备调用路径
4.1 写入路径
4.1.1 调用链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| TransactionManager::submit_transaction() │ ▼ Journal::submit_record() │ ▼ SegmentedJournal::record_submitter::submit() │ ▼ SegmentAllocator::allocate_segment() │ ▼ SegmentProvider::allocate_segment() │ ▼ SegmentManager::open(segment_id) │ ▼ Segment::write(offset, bufferlist) │ ▼ BlockSegmentManager::segment_write(paddr, bufferlist) │ ▼ do_writev(device_id, device, offset, bufferlist, block_size) │ ▼ seastar::file::dma_write(offset, iovec) │ ▼ Linux 系统调用: pwritev() / io_submit()
|
4.1.2 关键代码
Segment::write() (segment_manager/block.cc:372):
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Segment::write_ertr::future<> BlockSegment::write( segment_off_t offset, ceph::bufferlist bl) { auto paddr = paddr_t::make_seg_paddr(id, offset); if (offset < write_pointer || offset % manager.superblock.block_size != 0 || bl.length() % manager.superblock.block_size != 0) { return crimson::ct_error::invarg::make(); } write_pointer = offset + bl.length(); return manager.segment_write(paddr, bl); }
|
BlockSegmentManager::segment_write() (segment_manager/block.cc:425):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Segment::write_ertr::future<> BlockSegmentManager::segment_write( paddr_t addr, ceph::bufferlist bl, bool ignore_check) { assert(addr.get_device_id() == get_device_id()); assert((bl.length() % superblock.block_size) == 0); stats.data_write.increment(bl.length()); return do_writev( get_device_id(), device, get_offset(addr), std::move(bl), superblock.block_size); }
|
do_writev() (segment_manager/block.cc:87):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| static write_ertr::future<> do_writev( device_id_t device_id, seastar::file &device, uint64_t offset, bufferlist&& bl, size_t block_size) { bl.rebuild_aligned(block_size); return seastar::do_with( bl.prepare_iovs(), std::move(bl), [&device, device_id, offset](auto& iovs, auto& bl) { return write_ertr::parallel_for_each( iovs, [&device, device_id, offset](auto& p) mutable { auto off = offset + p.offset; auto len = p.length; auto& iov = p.iov; return device.dma_write(off, std::move(iov)) .handle_exception([device_id, off, len](auto e) { ERROR("dma_write got error -- {}", e); return crimson::ct_error::input_output_error::make(); }) .then([device_id, off, len](size_t written) { if (written != len) { ERROR("dma_write len inconsistent"); return crimson::ct_error::input_output_error::make(); } return write_ertr::now(); }); }); }); }
|
4.2 读取路径
4.2.1 调用链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Cache::read_extent() │ ▼ ExtentPlacementManager::read(paddr, len, bufferptr) │ ▼ Device::read(paddr, len, bufferptr) │ ▼ SegmentManager::read(paddr, len, bufferptr) │ ▼ do_read(device_id, device, offset, len, bufferptr) │ ▼ seastar::file::dma_read(offset, buffer, len) │ ▼ Linux 系统调用: preadv() / io_submit()
|
4.2.2 关键代码
do_read() (segment_manager/block.cc:137):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| static read_ertr::future<> do_read( device_id_t device_id, seastar::file &device, uint64_t offset, size_t len, bufferptr &bptr) { assert(len <= bptr.length()); return device.dma_read( offset, bptr.c_str(), len ).handle_exception([device_id, offset, len](auto e) { ERROR("dma_read got error -- {}", e); return crimson::ct_error::input_output_error::make(); }).then([device_id, offset, len](auto result) { if (result != len) { ERROR("read len inconsistent"); return crimson::ct_error::input_output_error::make(); } return read_ertr::now(); }); }
|
5. Seastar DMA 接口
5.1 文件对象创建
位置: segment_manager/block.cc:258
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| static open_device_ret open_device(const std::string &path) { return seastar::file_stat(path, seastar::follow_symlink::yes) .then([&path](auto stat) mutable { return seastar::open_file_dma( path, seastar::open_flags::rw | seastar::open_flags::dsync ).then([stat](auto file) mutable { return file.size().then([stat, file](auto size) mutable { stat.size = size; stat.block_size = file.disk_write_dma_alignment(); return std::make_pair(file, stat); }); }); }); }
|
5.2 DMA 操作特点
- 零拷贝: DMA 操作直接从用户缓冲区传输到内核,减少数据拷贝
- 异步 I/O: 所有操作都是异步的,返回
seastar::future
- 对齐要求: 缓冲区和偏移必须满足 DMA 对齐要求
- 批量操作: 支持
writev/readv 进行批量 I/O
5.3 Seastar 底层实现
Seastar 的 dma_read/dma_write 最终会调用:
- Linux AIO: 使用
io_submit() 和 io_getevents() 进行异步 I/O
- io_uring: 如果系统支持,使用更高效的 io_uring 接口
- 直接 I/O: 绕过页缓存,直接访问存储设备
6. 随机块设备调用路径
6.1 RandomBlockManager
位置: random_block_manager/block_rb_manager.cc
随机块设备也使用类似的 DMA 接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| RandomBlockManager::write_ertr::future<> RandomBlockManager::write( paddr_t addr, ceph::bufferptr &bptr) { auto offset = get_offset(addr); return device.dma_write(offset, bptr.c_str(), bptr.length()) .handle_exception([](auto e) { return crimson::ct_error::input_output_error::make(); }); }
|
7. 设备初始化流程
7.1 设备创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Device::make_device(device_path, device_type) │ ├─► 如果是 SEGMENTED 类型 │ │ │ ▼ │ SegmentManager::get_segment_manager() │ │ │ ├─► BlockSegmentManager::create() │ ├─► ZBDSegmentManager::create() │ └─► EphemeralSegmentManager::create() │ └─► 如果是 RANDOM_BLOCK 类型 │ ▼ RandomBlockManager::create()
|
7.2 设备挂载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Device::mount() │ ▼ BlockSegmentManager::mount() │ ▼ open_device(device_path) // 打开文件 │ ▼ read_superblock() // 读取超级块 │ ▼ tracker->read_in() // 读取段状态跟踪器 │ ▼ 设备就绪,可以接受 I/O
|
8. I/O 优化机制
8.1 批量 I/O
SeaStore 使用 writev/readv 进行批量 I/O 操作:
1 2 3 4 5 6 7
| auto iovs = bl.prepare_iovs();
return parallel_for_each(iovs, [&device](auto& iov) { return device.dma_write(offset, iov); });
|
8.2 对齐优化
所有 I/O 操作都按照块大小对齐:
1 2 3 4 5 6
| bl.rebuild_aligned(block_size);
assert(offset % block_size == 0); assert(len % block_size == 0);
|
8.3 异步 I/O
所有 I/O 操作都是异步的,使用 Seastar 的 future/promise 机制:
1 2 3 4 5
| return device.dma_write(offset, buffer, len) .then([](size_t written) { return seastar::now(); });
|
9. 错误处理
9.1 I/O 错误类型
input_output_error: 底层 I/O 错误
invarg: 无效参数(如未对齐)
enospc: 空间不足
erange: 地址超出范围
9.2 错误处理机制
1 2 3 4 5 6 7 8 9 10 11 12 13
| return device.dma_write(offset, buffer, len) .handle_exception([](auto e) { ERROR("dma_write got error -- {}", e); return crimson::ct_error::input_output_error::make(); }) .then([](size_t written) { if (written != len) { return crimson::ct_error::input_output_error::make(); } return write_ertr::now(); });
|
10. 性能特性
10.1 DMA 优势
- 零拷贝: 数据直接从用户缓冲区传输到设备,无需内核拷贝
- 异步操作: 不阻塞线程,充分利用 CPU
- 批量操作: 支持向量 I/O,减少系统调用次数
10.2 Seastar 优化
- 事件驱动: 基于 epoll/io_uring 的事件驱动模型
- 协程支持: 使用协程简化异步代码
- Shard 隔离: 每个 CPU 核心独立处理 I/O
11. 总结
SeaStore 的存储调用机制具有以下特点:
- 多层抽象: 从 Device 接口到具体实现,再到 Seastar DMA 接口
- 异步 I/O: 所有操作都是异步的,提高并发性能
- 零拷贝: 使用 DMA 减少数据拷贝开销
- 类型支持: 支持段式设备和随机块设备
- 错误处理: 完善的错误处理和恢复机制
通过这种设计,SeaStore 能够充分利用现代存储设备的性能,同时保持代码的清晰和可维护性。
正在加载留言…