SeaStore 硬盘存储调用机制

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;

// 后端类型(SEGMENTED 或 RANDOM_BLOCK)
virtual backend_type_t get_backend_type() const = 0;

// 块大小
virtual extent_len_t get_block_size() const = 0;
};

3.2 设备实现类型

SeaStore 支持两种主要的设备类型:

  1. 段式设备 (SEGMENTED):

    • BlockSegmentManager: 普通块设备
    • ZBDSegmentManager: Zoned Block Device (ZBD)
    • EphemeralSegmentManager: 临时内存设备(测试用)
  2. 随机块设备 (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, // seastar::file 对象
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, // Seastar 文件对象
uint64_t offset,
bufferlist&& bl,
size_t block_size)
{
// 确保缓冲区按块大小对齐
bl.rebuild_aligned(block_size);

return seastar::do_with(
bl.prepare_iovs(), // 准备 I/O 向量
std::move(bl),
[&device, device_id, offset](auto& iovs, auto& bl)
{
// 并行写入多个 I/O 向量
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;

// 调用 Seastar 的 DMA 写入接口
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());

// 调用 Seastar 的 DMA 读取接口
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 {
// 以 DMA 模式打开文件
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;
// 获取 DMA 写入对齐要求
stat.block_size = file.disk_write_dma_alignment();
return std::make_pair(file, stat);
});
});
});
}

5.2 DMA 操作特点

  1. 零拷贝: DMA 操作直接从用户缓冲区传输到内核,减少数据拷贝
  2. 异步 I/O: 所有操作都是异步的,返回 seastar::future
  3. 对齐要求: 缓冲区和偏移必须满足 DMA 对齐要求
  4. 批量操作: 支持 writev/readv 进行批量 I/O

5.3 Seastar 底层实现

Seastar 的 dma_read/dma_write 最终会调用:

  1. Linux AIO: 使用 io_submit()io_getevents() 进行异步 I/O
  2. io_uring: 如果系统支持,使用更高效的 io_uring 接口
  3. 直接 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);

// 调用 DMA 写入
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
// 准备多个 I/O 向量
auto iovs = bl.prepare_iovs();

// 并行执行多个 I/O
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) {
// I/O 完成后的处理
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 优势

  1. 零拷贝: 数据直接从用户缓冲区传输到设备,无需内核拷贝
  2. 异步操作: 不阻塞线程,充分利用 CPU
  3. 批量操作: 支持向量 I/O,减少系统调用次数

10.2 Seastar 优化

  1. 事件驱动: 基于 epoll/io_uring 的事件驱动模型
  2. 协程支持: 使用协程简化异步代码
  3. Shard 隔离: 每个 CPU 核心独立处理 I/O

11. 总结

SeaStore 的存储调用机制具有以下特点:

  1. 多层抽象: 从 Device 接口到具体实现,再到 Seastar DMA 接口
  2. 异步 I/O: 所有操作都是异步的,提高并发性能
  3. 零拷贝: 使用 DMA 减少数据拷贝开销
  4. 类型支持: 支持段式设备和随机块设备
  5. 错误处理: 完善的错误处理和恢复机制

通过这种设计,SeaStore 能够充分利用现代存储设备的性能,同时保持代码的清晰和可维护性。

文章互动

阅读 --

留言

0 条留言

正在加载留言…