1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (C) 2023 Intel Corporation.
3 * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES.
4 * All rights reserved.
5 */
6
7 #include "spdk/accel.h"
8 #include "spdk/accel_module.h"
9 #include "spdk/thread.h"
10
11 static struct spdk_accel_module_if g_ex_module;
12
13 struct ex_accel_io_channel {
14 struct spdk_poller *completion_poller;
15 STAILQ_HEAD(, spdk_accel_task) tasks_to_complete;
16 };
17
18 static int
ex_accel_copy_iovs(struct iovec * dst_iovs,uint32_t dst_iovcnt,struct iovec * src_iovs,uint32_t src_iovcnt)19 ex_accel_copy_iovs(struct iovec *dst_iovs, uint32_t dst_iovcnt,
20 struct iovec *src_iovs, uint32_t src_iovcnt)
21 {
22 struct spdk_ioviter iter;
23 void *src, *dst;
24 size_t len;
25
26 for (len = spdk_ioviter_first(&iter, src_iovs, src_iovcnt,
27 dst_iovs, dst_iovcnt, &src, &dst);
28 len != 0;
29 len = spdk_ioviter_next(&iter, &src, &dst)) {
30 memcpy(dst, src, len);
31 }
32
33 return 0;
34 }
35
36 static int
ex_accel_compare(struct iovec * src_iovs,uint32_t src_iovcnt,struct iovec * src2_iovs,uint32_t src2_iovcnt)37 ex_accel_compare(struct iovec *src_iovs, uint32_t src_iovcnt,
38 struct iovec *src2_iovs, uint32_t src2_iovcnt)
39 {
40 if (spdk_unlikely(src_iovcnt != 1 || src2_iovcnt != 1)) {
41 return -EINVAL;
42 }
43
44 if (spdk_unlikely(src_iovs[0].iov_len != src2_iovs[0].iov_len)) {
45 return -EINVAL;
46 }
47
48 return memcmp(src_iovs[0].iov_base, src2_iovs[0].iov_base, src_iovs[0].iov_len);
49 }
50
51 static int
ex_accel_fill(struct iovec * iovs,uint32_t iovcnt,uint8_t fill)52 ex_accel_fill(struct iovec *iovs, uint32_t iovcnt, uint8_t fill)
53 {
54 void *dst;
55 size_t nbytes;
56
57 if (spdk_unlikely(iovcnt != 1)) {
58 fprintf(stderr, "Unexpected number of iovs: %" PRIu32 "\n", iovcnt);
59 return -EINVAL;
60 }
61
62 dst = iovs[0].iov_base;
63 nbytes = iovs[0].iov_len;
64
65 memset(dst, fill, nbytes);
66
67 return 0;
68 }
69
70 static int
ex_accel_comp_poll(void * arg)71 ex_accel_comp_poll(void *arg)
72 {
73 struct ex_accel_io_channel *ex_ch = arg;
74 STAILQ_HEAD(, spdk_accel_task) tasks_to_complete;
75 struct spdk_accel_task *accel_task;
76
77 if (STAILQ_EMPTY(&ex_ch->tasks_to_complete)) {
78 return SPDK_POLLER_IDLE;
79 }
80
81 STAILQ_INIT(&tasks_to_complete);
82 STAILQ_SWAP(&tasks_to_complete, &ex_ch->tasks_to_complete, spdk_accel_task);
83
84 while ((accel_task = STAILQ_FIRST(&tasks_to_complete))) {
85 STAILQ_REMOVE_HEAD(&tasks_to_complete, link);
86 spdk_accel_task_complete(accel_task, accel_task->status);
87 }
88
89 return SPDK_POLLER_BUSY;
90 }
91
92 static int
ex_accel_create_cb(void * io_device,void * ctx_buf)93 ex_accel_create_cb(void *io_device, void *ctx_buf)
94 {
95 struct ex_accel_io_channel *ex_ch = ctx_buf;
96
97 STAILQ_INIT(&ex_ch->tasks_to_complete);
98 ex_ch->completion_poller = SPDK_POLLER_REGISTER(ex_accel_comp_poll, ex_ch, 0);
99
100 return 0;
101 }
102
103 static void
ex_accel_destroy_cb(void * io_device,void * ctx_buf)104 ex_accel_destroy_cb(void *io_device, void *ctx_buf)
105 {
106 struct ex_accel_io_channel *ex_ch = ctx_buf;
107
108 spdk_poller_unregister(&ex_ch->completion_poller);
109 }
110
111 static int
ex_accel_module_init(void)112 ex_accel_module_init(void)
113 {
114 spdk_io_device_register(&g_ex_module, ex_accel_create_cb, ex_accel_destroy_cb,
115 sizeof(struct ex_accel_io_channel), "external_accel_module");
116
117 return 0;
118 }
119
120 static void
ex_accel_module_fini(void * ctx)121 ex_accel_module_fini(void *ctx)
122 {
123 spdk_io_device_unregister(&g_ex_module, NULL);
124 spdk_accel_module_finish();
125 }
126
127 static size_t
ex_accel_module_get_ctx_size(void)128 ex_accel_module_get_ctx_size(void)
129 {
130 return sizeof(struct spdk_accel_task);
131 }
132
133 inline static void
add_to_comp_list(struct ex_accel_io_channel * ex_ch,struct spdk_accel_task * accel_task)134 add_to_comp_list(struct ex_accel_io_channel *ex_ch, struct spdk_accel_task *accel_task)
135 {
136 STAILQ_INSERT_TAIL(&ex_ch->tasks_to_complete, accel_task, link);
137 }
138
139 static bool
ex_accel_supports_opcode(enum spdk_accel_opcode opc)140 ex_accel_supports_opcode(enum spdk_accel_opcode opc)
141 {
142 switch (opc) {
143 case SPDK_ACCEL_OPC_COPY:
144 case SPDK_ACCEL_OPC_FILL:
145 case SPDK_ACCEL_OPC_COMPARE:
146 return true;
147 default:
148 return false;
149 }
150 }
151
152 static struct spdk_io_channel *
ex_accel_get_io_channel(void)153 ex_accel_get_io_channel(void)
154 {
155 return spdk_get_io_channel(&g_ex_module);
156 }
157
158 static int
ex_accel_submit_tasks(struct spdk_io_channel * ch,struct spdk_accel_task * accel_task)159 ex_accel_submit_tasks(struct spdk_io_channel *ch, struct spdk_accel_task *accel_task)
160 {
161 struct ex_accel_io_channel *ex_ch = spdk_io_channel_get_ctx(ch);
162
163 printf("Running on accel module task with code: %" PRIu8 "\n", accel_task->op_code);
164 switch (accel_task->op_code) {
165 case SPDK_ACCEL_OPC_COPY:
166 accel_task->status = ex_accel_copy_iovs(accel_task->d.iovs, accel_task->d.iovcnt,
167 accel_task->s.iovs, accel_task->s.iovcnt);
168 break;
169 case SPDK_ACCEL_OPC_FILL:
170 accel_task->status = ex_accel_fill(accel_task->d.iovs, accel_task->d.iovcnt,
171 accel_task->fill_pattern);
172 break;
173 case SPDK_ACCEL_OPC_COMPARE:
174 accel_task->status = ex_accel_compare(accel_task->s.iovs, accel_task->s.iovcnt,
175 accel_task->s2.iovs, accel_task->s2.iovcnt);
176 break;
177 default:
178 fprintf(stderr, "Unsupported accel opcode: %" PRIu8 "\n", accel_task->op_code);
179 accel_task->status = 1;
180 break;
181 }
182
183 add_to_comp_list(ex_ch, accel_task);
184
185 return accel_task->status;
186 }
187
188 static struct spdk_accel_module_if g_ex_module = {
189 .module_init = ex_accel_module_init,
190 .module_fini = ex_accel_module_fini,
191 .get_ctx_size = ex_accel_module_get_ctx_size,
192 .name = "external",
193 .supports_opcode = ex_accel_supports_opcode,
194 .get_io_channel = ex_accel_get_io_channel,
195 .submit_tasks = ex_accel_submit_tasks,
196 };
197
198 SPDK_ACCEL_MODULE_REGISTER(external, &g_ex_module)
199