SPDK调用DPDK流程分析文档

SPDK调用DPDK流程分析文档

目录

  1. 概述
  2. 初始化流程
  3. 主要调用点
  4. 环境抽象层
  5. 关键调用流程详解
  6. DPDK功能映射

概述

SPDK与DPDK的关系

SPDK (Storage Performance Development Kit) 使用DPDK (Data Plane Development Kit) 作为其底层环境抽象层。SPDK通过 env_dpdk 模块封装了DPDK的功能,为上层存储应用提供统一的环境抽象接口。

设计目的

  1. 内存管理: 使用DPDK的大页内存管理
  2. CPU核心管理: 利用DPDK的CPU亲和性功能
  3. PCI设备访问: 通过DPDK的PCI总线访问硬件
  4. 线程管理: 使用DPDK的轻量级线程模型
  5. 中断处理: 利用DPDK的中断机制

初始化流程

完整初始化流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
应用程序启动

spdk_app_start()

app_setup_env() [event/app.c]

spdk_env_init() [env_dpdk/init.c]

build_eal_cmdline() [构建DPDK EAL参数]

rte_eal_init() [DPDK EAL初始化]

spdk_env_dpdk_post_init() [SPDK环境后初始化]

pci_env_init() [PCI环境初始化]
mem_map_init() [内存映射初始化]
vtophys_init() [虚拟地址到物理地址映射初始化]

详细步骤

1. 应用程序入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// event/app.c
int spdk_app_start(struct spdk_app_opts *opts, spdk_msg_fn start_fn, void *arg1)
{
// ...

// 1. 设置环境
rc = app_setup_env(opts);
if (rc < 0) {
return rc;
}

// 2. 创建reactor
rc = spdk_reactors_init();

// 3. 启动应用
// ...
}

2. 环境设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// event/app.c
static int app_setup_env(struct spdk_app_opts *opts)
{
struct spdk_env_opts env_opts = {};

// 1. 初始化环境选项
spdk_env_opts_init(&env_opts);

// 2. 填充选项
env_opts.name = opts->name;
env_opts.core_mask = opts->reactor_mask;
env_opts.shm_id = opts->shm_id;
env_opts.mem_channel = opts->mem_channel;
env_opts.master_core = opts->master_core;
env_opts.mem_size = opts->mem_size;
// ...

// 3. 初始化环境
rc = spdk_env_init(&env_opts);

return rc;
}

3. DPDK EAL初始化

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
// env_dpdk/init.c
int spdk_env_init(const struct spdk_env_opts *opts)
{
char **dpdk_args = NULL;
int rc;

// 1. 构建DPDK EAL命令行参数
rc = build_eal_cmdline(opts);
if (rc < 0) {
return -EINVAL;
}

// 2. 复制参数数组(DPDK会修改)
dpdk_args = calloc(g_eal_cmdline_argcount, sizeof(char *));
memcpy(dpdk_args, g_eal_cmdline, sizeof(char *) * g_eal_cmdline_argcount);

// 3. 调用DPDK EAL初始化
optind = 1; // 重置getopt
rc = rte_eal_init(g_eal_cmdline_argcount, dpdk_args);
optind = orig_optind;

if (rc < 0) {
return -rte_errno;
}

// 4. SPDK后初始化
rc = spdk_env_dpdk_post_init(legacy_mem);

return rc;
}

主要调用点

1. EAL初始化 (rte_eal_init)

调用位置: env_dpdk/init.c:567

作用: 初始化DPDK环境抽象层(Environment Abstraction Layer)

参数构建过程:

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
41
42
43
44
45
46
// env_dpdk/init.c
static int build_eal_cmdline(const struct spdk_env_opts *opts)
{
char **args = NULL;
int argcount = 0;

// 程序名
args = push_arg(args, &argcount, _sprintf_alloc("%s", opts->name));

// CPU核心掩码
args = push_arg(args, &argcount, _sprintf_alloc("-c %s", opts->core_mask));

// 内存通道数
if (opts->mem_channel > 0) {
args = push_arg(args, &argcount, _sprintf_alloc("-n %d", opts->mem_channel));
}

// 内存大小
if (opts->mem_size >= 0) {
args = push_arg(args, &argcount, _sprintf_alloc("-m %d", opts->mem_size));
}

// Master核心
if (opts->master_core > 0) {
args = push_arg(args, &argcount, _sprintf_alloc("--master-lcore=%d", opts->master_core));
}

// PCI黑名单/白名单
if (opts->num_pci_addr > 0) {
// ... 添加PCI地址列表
}

// IOMMU模式
if (opts->iova_mode) {
args = push_arg(args, &argcount, _sprintf_alloc("--iova-mode=%s", opts->iova_mode));
}

// 基础虚拟地址
args = push_arg(args, &argcount, _sprintf_alloc("--base-virtaddr=0x%" PRIx64, opts->base_virtaddr));

// 其他选项...

g_eal_cmdline = args;
g_eal_cmdline_argcount = argcount;
return argcount;
}

构建的参数示例:

1
2
3
["spdk", "-c", "0x1", "-n", "4", "-m", "1024", 
"--master-lcore=0", "--base-virtaddr=0x200000000000",
"--file-prefix=spdk_pid1234", "--log-level=lib.eal:6", ...]

2. 内存管理调用

内存分配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// env_dpdk/env.c
void *spdk_malloc(size_t size, size_t align, uint64_t *phys_addr,
int socket_id, uint32_t flags)
{
void *buf;

// 使用DPDK的内存分配
align = spdk_max(align, RTE_CACHE_LINE_SIZE);
buf = rte_malloc_socket(NULL, size, align, socket_id);

// 获取物理地址
if (buf && phys_addr) {
*phys_addr = virt_to_phys(buf);
}

return buf;
}

虚拟地址到物理地址转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// env_dpdk/env.c
static uint64_t virt_to_phys(void *vaddr)
{
uint64_t ret;

// 首先尝试使用DPDK的转换
ret = rte_malloc_virt2iova(vaddr);
if (ret != RTE_BAD_IOVA) {
return ret;
}

// 回退到SPDK自己的实现
return spdk_vtophys(vaddr, NULL);
}

Memzone管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// env_dpdk/env.c
void *spdk_memzone_reserve_aligned(const char *name, size_t len,
int socket_id, unsigned flags, unsigned align)
{
const struct rte_memzone *mz;
unsigned dpdk_flags = 0;

// 转换SPDK标志到DPDK标志
if ((flags & SPDK_MEMZONE_NO_IOVA_CONTIG) == 0) {
dpdk_flags |= RTE_MEMZONE_IOVA_CONTIG;
}

// 使用DPDK的memzone分配
mz = rte_memzone_reserve_aligned(name, len, socket_id, dpdk_flags, align);

if (mz != NULL) {
memset(mz->addr, 0, len);
return mz->addr;
}

return NULL;
}

3. CPU核心管理调用

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
// env_dpdk/threads.c
uint32_t spdk_env_get_current_core(void)
{
// 直接调用DPDK API
return rte_lcore_id();
}

uint32_t spdk_env_get_core_count(void)
{
return rte_lcore_count();
}

uint32_t spdk_env_get_next_core(uint32_t prev_core)
{
unsigned lcore = rte_get_next_lcore(prev_core, 0, 0);
if (lcore == RTE_MAX_LCORE) {
return UINT32_MAX;
}
return lcore;
}

int spdk_env_thread_launch_pinned(uint32_t core, thread_start_fn fn, void *arg)
{
// 使用DPDK的远程启动功能
return rte_eal_remote_launch(fn, arg, core);
}

void spdk_env_thread_wait_all(void)
{
rte_eal_mp_wait_lcore();
}

4. PCI设备管理调用

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
// env_dpdk/pci.c
static int map_bar_rte(struct spdk_pci_device *device, uint32_t bar,
void **mapped_addr, uint64_t *phys_addr, uint64_t *size)
{
struct rte_pci_device *dev = device->dev_handle;

// 直接使用DPDK PCI设备的信息
*mapped_addr = dev->mem_resource[bar].addr;
*phys_addr = (uint64_t)dev->mem_resource[bar].phys_addr;
*size = (uint64_t)dev->mem_resource[bar].len;

return 0;
}

static int cfg_read_rte(struct spdk_pci_device *dev, void *value,
uint32_t len, uint32_t offset)
{
// 使用DPDK的PCI配置空间读取
int rc = rte_pci_read_config(dev->dev_handle, value, len, offset);
return (rc > 0 && (uint32_t) rc == len) ? 0 : -1;
}

static int cfg_write_rte(struct spdk_pci_device *dev, void *value,
uint32_t len, uint32_t offset)
{
// 使用DPDK的PCI配置空间写入
int rc = rte_pci_write_config(dev->dev_handle, value, len, offset);
return (rc > 0 && (uint32_t) rc == len) ? 0 : -1;
}

环境抽象层

SPDK环境抽象接口

SPDK定义了一组环境抽象接口,env_dpdk模块实现了这些接口:

内存管理接口

1
2
3
4
5
6
7
// spdk/env.h
void *spdk_malloc(size_t size, size_t align, uint64_t *phys_addr,
int socket_id, uint32_t flags);
void spdk_free(void *buf);
void *spdk_dma_malloc(size_t size, size_t align, uint64_t *phys_addr);
void spdk_dma_free(void *buf);
void *spdk_memzone_reserve(const char *name, size_t len, int socket_id, unsigned flags);

CPU核心接口

1
2
3
4
5
uint32_t spdk_env_get_current_core(void);
uint32_t spdk_env_get_core_count(void);
uint32_t spdk_env_get_next_core(uint32_t prev_core);
int spdk_env_thread_launch_pinned(uint32_t core, thread_start_fn fn, void *arg);
void spdk_env_thread_wait_all(void);

PCI设备接口

1
2
3
4
5
6
int spdk_pci_device_map_bar(struct spdk_pci_device *dev, uint32_t bar, 
void **mapped_addr, uint64_t *phys_addr, uint64_t *size);
int spdk_pci_device_cfg_read(struct spdk_pci_device *dev, void *value,
uint32_t len, uint32_t offset);
int spdk_pci_device_cfg_write(struct spdk_pci_device *dev, void *value,
uint32_t len, uint32_t offset);

关键调用流程详解

流程1: DPDK初始化流程

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
1. 应用程序调用 spdk_app_start()

2. app_setup_env() 设置环境选项

3. spdk_env_init() 开始初始化

4. build_eal_cmdline() 构建DPDK命令行参数
├── 添加程序名
├── 添加CPU核心掩码 (-c)
├── 添加内存通道数 (-n)
├── 添加内存大小 (-m)
├── 添加master核心 (--master-lcore)
├── 添加PCI黑名单/白名单
├── 添加IOMMU模式 (--iova-mode)
├── 添加基础虚拟地址 (--base-virtaddr)
├── 添加文件前缀 (--file-prefix)
└── 添加日志级别 (--log-level)

5. rte_eal_init() 调用DPDK EAL初始化
├── DPDK解析命令行参数
├── DPDK分配大页内存
├── DPDK初始化PCI总线
├── DPDK设置CPU亲和性
└── DPDK初始化其他子系统

6. spdk_env_dpdk_post_init() SPDK后初始化
├── pci_env_init() PCI环境初始化
├── mem_map_init() 内存映射初始化
└── vtophys_init() 虚拟地址转换初始化

7. 初始化完成

流程2: 内存分配流程

1
2
3
4
5
6
7
8
9
应用程序调用 spdk_malloc()

spdk_malloc() [env_dpdk/env.c]
├── 对齐到缓存行
├── 调用 rte_malloc_socket() [DPDK API]
├── 调用 virt_to_phys()
│ ├── 尝试 rte_malloc_virt2iova() [DPDK API]
│ └── 回退到 spdk_vtophys()
└── 返回分配的缓冲区

流程3: PCI设备枚举流程

1
2
3
4
5
6
7
8
9
10
11
12
13
spdk_pci_enumerate()

DPDK PCI总线枚举
├── rte_eal_init() 时已经初始化PCI总线
├── SPDK注册PCI驱动
│ └── rte_pci_register() [DPDK API]
├── DPDK扫描PCI设备
│ └── rte_eal_scan_proc() [内部调用]
├── DPDK匹配驱动和设备
└── 调用SPDK的驱动probe函数
└── pci_device_init()
├── map_bar_rte() [使用DPDK PCI设备信息]
└── vtophys_pci_device_added() [注册到地址转换]

流程4: 线程启动流程

1
2
3
4
5
6
7
8
spdk_env_thread_launch_pinned()

rte_eal_remote_launch() [DPDK API]
├── DPDK检查核心是否有效
├── DPDK检查核心是否已启动
├── 通过管道发送启动消息
└── 目标核心接收消息并启动线程
└── 在目标核心上执行函数

DPDK功能映射

内存管理映射

SPDK函数 DPDK函数 说明
spdk_malloc() rte_malloc_socket() 内存分配
spdk_free() rte_free() 内存释放
spdk_realloc() rte_realloc() 内存重分配
spdk_memzone_reserve() rte_memzone_reserve_aligned() 大页内存预留
spdk_vtophys() rte_malloc_virt2iova() 虚拟地址转物理地址

CPU核心管理映射

SPDK函数 DPDK函数 说明
spdk_env_get_current_core() rte_lcore_id() 获取当前核心ID
spdk_env_get_core_count() rte_lcore_count() 获取核心数量
spdk_env_get_next_core() rte_get_next_lcore() 获取下一个核心
spdk_env_get_socket_id() rte_lcore_to_socket_id() 获取Socket ID
spdk_env_thread_launch_pinned() rte_eal_remote_launch() 启动绑定核心的线程
spdk_env_thread_wait_all() rte_eal_mp_wait_lcore() 等待所有线程

PCI设备管理映射

SPDK函数 DPDK函数/结构 说明
spdk_pci_device_map_bar() rte_pci_device.mem_resource[] 映射BAR空间
spdk_pci_device_cfg_read() rte_pci_read_config() 读取PCI配置空间
spdk_pci_device_cfg_write() rte_pci_write_config() 写入PCI配置空间
spdk_pci_enumerate() rte_bus_scan(), rte_bus_probe() 枚举PCI设备

其他功能映射

SPDK函数 DPDK函数 说明
spdk_get_ticks() rte_rdtsc() 获取时间戳计数器
spdk_delay_us() rte_delay_us() 微秒延迟
spdk_delay_ms() rte_delay_ms() 毫秒延迟

DPDK初始化参数详解

核心参数构建过程

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// env_dpdk/init.c: build_eal_cmdline()

// 1. CPU核心掩码
if (opts->core_mask[0] == '[') {
// 核心列表格式: [0,1,2-4]
args = push_arg(args, &argcount, _sprintf_alloc("-l %s", opts->core_mask + 1));
} else {
// 十六进制掩码: 0x1
args = push_arg(args, &argcount, _sprintf_alloc("-c %s", opts->core_mask));
}

// 2. 内存通道数(NUMA节点数)
if (opts->mem_channel > 0) {
args = push_arg(args, &argcount, _sprintf_alloc("-n %d", opts->mem_channel));
}

// 3. 内存大小(MB)
if (opts->mem_size >= 0) {
args = push_arg(args, &argcount, _sprintf_alloc("-m %d", opts->mem_size));
}

// 4. Master核心
if (opts->master_core > 0) {
args = push_arg(args, &argcount, _sprintf_alloc("--master-lcore=%d", opts->master_core));
}

// 5. IOMMU模式
if (opts->iova_mode) {
args = push_arg(args, &argcount, _sprintf_alloc("--iova-mode=%s", opts->iova_mode));
} else {
// 自动检测IOMMU能力
if (get_iommu_width() < SPDK_IOMMU_VA_REQUIRED_WIDTH) {
args = push_arg(args, &argcount, _sprintf_alloc("--iova-mode=pa"));
}
}

// 6. 基础虚拟地址
args = push_arg(args, &argcount, _sprintf_alloc("--base-virtaddr=0x%" PRIx64, opts->base_virtaddr));

// 7. 文件前缀(用于共享内存)
if (opts->shm_id < 0) {
args = push_arg(args, &argcount, _sprintf_alloc("--file-prefix=spdk_pid%d", getpid()));
} else {
args = push_arg(args, &argcount, _sprintf_alloc("--file-prefix=spdk%d", opts->shm_id));
args = push_arg(args, &argcount, strdup("--proc-type=auto"));
}

// 8. 内存分配匹配
#if RTE_VERSION >= RTE_VERSION_NUM(19, 02, 0, 0)
args = push_arg(args, &argcount, strdup("--match-allocations"));
#endif

// 9. PCI黑名单/白名单
if (opts->num_pci_addr > 0) {
for (i = 0; i < opts->num_pci_addr; i++) {
spdk_pci_addr_fmt(bdf, 32, &pci_addr[i]);
args = push_arg(args, &argcount, _sprintf_alloc("%s=%s",
(opts->pci_blacklist ? "--pci-blacklist" : "--pci-whitelist"), bdf));
}
}

// 10. 日志级别
args = push_arg(args, &argcount, strdup("--log-level=lib.eal:6"));
args = push_arg(args, &argcount, strdup("--log-level=lib.cryptodev:5"));
args = push_arg(args, &argcount, strdup("--log-level=user1:6"));

// 11. 用户自定义参数
if (opts->env_context) {
args = push_arg(args, &argcount, strdup(opts->env_context));
}

DPDK后初始化流程

spdk_env_dpdk_post_init()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// env_dpdk/init.c
int spdk_env_dpdk_post_init(bool legacy_mem)
{
int rc;

// 1. PCI环境初始化
pci_env_init();

// 2. 内存映射初始化
rc = mem_map_init(legacy_mem);
if (rc < 0) {
return rc;
}

// 3. 虚拟地址到物理地址转换初始化
rc = vtophys_init();
if (rc < 0) {
return rc;
}

return 0;
}

PCI环境初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
// env_dpdk/pci.c
void pci_env_init(void)
{
// 注册PCI设备热插拔回调
rte_pci_register_driver(&g_spdk_pci_driver);

// 扫描PCI设备
rte_bus_scan();
rte_bus_probe();

// 设置热插拔回调
rte_eal_alarm_set(...);
}

内存映射初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// env_dpdk/memory.c
int mem_map_init(bool legacy_mem)
{
// 创建内存映射结构
// 用于虚拟地址到物理地址转换
g_mem_reg_map = spdk_mem_map_alloc(0, ...);

// 注册映射操作
spdk_mem_map_register(&g_mem_reg_map, ...);

// 初始化VFIO(如果启用)
#if VFIO_ENABLED
vfio_init();
#endif
}

运行时调用示例

示例1: 内存分配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 应用程序代码
void *buf;
uint64_t phys_addr;

// SPDK内存分配
buf = spdk_dma_malloc(4096, 64, &phys_addr);

// 实际调用链:
// spdk_dma_malloc()
// └─> spdk_malloc(size, align, phys_addr, socket_id, flags)
// └─> rte_malloc_socket(NULL, size, align, socket_id) [DPDK]
// └─> DPDK从大页内存池分配
// └─> rte_malloc_virt2iova(buf) [DPDK]
// └─> 返回IOVA地址

示例2: 线程启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 应用程序代码
void worker_thread(void *arg)
{
// 在工作核心上执行
uint32_t core = spdk_env_get_current_core();
printf("Running on core %u\n", core);
}

// 启动线程
spdk_env_thread_launch_pinned(2, worker_thread, NULL);
spdk_env_thread_wait_all();

// 实际调用链:
// spdk_env_thread_launch_pinned()
// └─> rte_eal_remote_launch(fn, arg, core) [DPDK]
// └─> DPDK通过管道发送消息到目标核心
// └─> 目标核心接收消息
// └─> 在目标核心上执行函数

示例3: PCI设备访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// SPDK PCI驱动注册
static struct spdk_pci_driver nvme_driver = {
.name = "spdk_nvme",
.id_table = nvme_id_table,
};

spdk_pci_register_driver(&nvme_driver);

// 实际调用链:
// spdk_pci_register_driver()
// └─> rte_pci_register(&driver->driver) [DPDK]
// └─> DPDK将驱动添加到驱动列表
// └─> DPDK扫描PCI设备
// └─> rte_bus_scan() [DPDK]
// └─> DPDK匹配驱动和设备
// └─> rte_bus_probe() [DPDK]
// └─> 调用驱动的probe函数
// └─> pci_device_init()
// └─> 使用DPDK的PCI设备信息

DPDK与SPDK的交互点

1. 内存池 (Mempool)

1
2
3
4
5
6
7
8
9
10
11
12
13
// env_dpdk/env.c
void *spdk_mempool_create(const char *name, unsigned count, unsigned ele_size,
unsigned cache_size, int socket_id)
{
struct rte_mempool *mp;

// 使用DPDK的mempool
mp = rte_mempool_create(name, count, ele_size, cache_size,
0, NULL, NULL, obj_init, obj_init_arg,
NULL, socket_id, 0);

return mp;
}

2. 中断处理

1
2
3
4
// 使用DPDK的alarm机制
rte_eal_alarm_set(us, callback, arg);

// 使用DPDK的管道进行线程间通信

3. 时间管理

1
2
3
4
5
6
7
// env_dpdk/env.c (间接使用)
uint64_t spdk_get_ticks(void)
{
// SPDK可能使用DPDK的rte_rdtsc()
// 或自己的实现
return __rdtsc();
}

关键数据结构

SPDK环境选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// spdk/env.h
struct spdk_env_opts {
const char *name; // 进程名
const char *core_mask; // CPU核心掩码
int shm_id; // 共享内存ID
int mem_channel; // 内存通道数
int master_core; // Master核心
int mem_size; // 内存大小(MB)
bool hugepage_single_segments; // 单文件段
bool unlink_hugepage; // 卸载时删除大页
const char *hugedir; // 大页目录
bool no_pci; // 禁用PCI
size_t num_pci_addr; // PCI地址数量
struct spdk_pci_addr *pci_blacklist; // PCI黑名单
struct spdk_pci_addr *pci_whitelist; // PCI白名单
const char *env_context; // 自定义环境上下文
const char *iova_mode; // IOMMU模式
uint64_t base_virtaddr; // 基础虚拟地址
};

DPDK PCI设备结构

1
2
3
4
5
6
7
8
// SPDK通过DPDK的PCI设备结构访问硬件
struct rte_pci_device {
struct rte_device device;
struct rte_pci_addr addr; // PCI地址
struct rte_mem_resource mem_resource[PCI_MAX_RESOURCE]; // BAR资源
uint16_t id; // 设备ID
// ...
};

调用流程时序图

初始化时序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
应用程序          event/app.c        env_dpdk/init.c         DPDK
| | | |
|-- spdk_app_start()| | |
| | | |
|-- app_setup_env()| | |
| |-- spdk_env_init()--| |
| | | |
| |-- build_eal_cmdline() |
| | | |
| |-- rte_eal_init()------------------>| |
| | | |-- 初始化EAL
| | | |-- 分配大页内存
| | | |-- 初始化PCI总线
| | | |-- 设置CPU亲和性
| |<-------------------| |
| | | |
| |-- spdk_env_dpdk_post_init() |
| | |-- pci_env_init() |
| | |-- mem_map_init() |
| | |-- vtophys_init() |
| | | |
|<------------------| | |

内存分配时序

1
2
3
4
5
6
7
8
9
10
应用程序          env_dpdk/env.c          DPDK
| | |
|-- spdk_malloc()--| |
| |-- rte_malloc_socket()------------>|
| | |-- 从大页内存分配
| |<-------------------|
| |-- virt_to_phys() |
| | |-- rte_malloc_virt2iova()--->|
| | |<----------------|
|<------------------| |

总结

SPDK调用DPDK的关键点

  1. 初始化入口: rte_eal_init() - DPDK EAL初始化
  2. 内存管理: rte_malloc_*(), rte_memzone_*() - 大页内存管理
  3. CPU管理: rte_lcore_*(), rte_eal_remote_launch() - CPU核心管理
  4. PCI管理: rte_pci_*(), rte_bus_*() - PCI设备管理
  5. 时间管理: rte_rdtsc(), rte_delay_*() - 时间相关功能

SPDK的封装策略

  1. 统一接口: 通过SPDK环境抽象层提供统一接口
  2. 参数转换: 将SPDK参数转换为DPDK参数
  3. 功能扩展: 在DPDK基础上添加SPDK特定功能(如vtophys)
  4. 错误处理: 封装DPDK错误码为SPDK错误码

调用特点

  1. 直接调用: SPDK直接调用DPDK API,无中间层
  2. 参数适配: SPDK参数经过转换后传递给DPDK
  3. 后处理: DPDK初始化后,SPDK进行额外的初始化
  4. 功能复用: SPDK充分利用DPDK的基础功能,避免重复实现

文档版本: 1.0
最后更新: 2024年

文章互动

阅读 --

留言

0 条留言

正在加载留言…