xref: /spdk/module/accel/dsa/accel_dsa.c (revision efb48abcb1422a7bd4c057370a62d274791f68d9)
1 /*   SPDX-License-Identifier: BSD-3-Clause
2  *   Copyright (C) 2022 Intel Corporation.
3  *   Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES.
4  *   All rights reserved.
5  */
6 
7 #include "accel_dsa.h"
8 
9 #include "spdk/stdinc.h"
10 
11 #include "spdk/accel_module.h"
12 #include "spdk/log.h"
13 #include "spdk_internal/idxd.h"
14 
15 #include "spdk/env.h"
16 #include "spdk/event.h"
17 #include "spdk/likely.h"
18 #include "spdk/thread.h"
19 #include "spdk/idxd.h"
20 #include "spdk/util.h"
21 #include "spdk/json.h"
22 #include "spdk/trace.h"
23 #include "spdk_internal/trace_defs.h"
24 
25 static bool g_dsa_enable = false;
26 static bool g_kernel_mode = false;
27 
28 enum channel_state {
29 	IDXD_CHANNEL_ACTIVE,
30 	IDXD_CHANNEL_ERROR,
31 };
32 
33 static bool g_dsa_initialized = false;
34 
35 struct idxd_device {
36 	struct				spdk_idxd_device *dsa;
37 	TAILQ_ENTRY(idxd_device)	tailq;
38 };
39 static TAILQ_HEAD(, idxd_device) g_dsa_devices = TAILQ_HEAD_INITIALIZER(g_dsa_devices);
40 static struct idxd_device *g_next_dev = NULL;
41 static uint32_t g_num_devices = 0;
42 static pthread_mutex_t g_dev_lock = PTHREAD_MUTEX_INITIALIZER;
43 
44 struct idxd_task {
45 	struct spdk_accel_task	task;
46 	struct idxd_io_channel	*chan;
47 };
48 
49 struct idxd_io_channel {
50 	struct spdk_idxd_io_channel	*chan;
51 	struct idxd_device		*dev;
52 	enum channel_state		state;
53 	struct spdk_poller		*poller;
54 	uint32_t			num_outstanding;
55 	TAILQ_HEAD(, spdk_accel_task)	queued_tasks;
56 };
57 
58 static struct spdk_io_channel *dsa_get_io_channel(void);
59 
60 static struct idxd_device *
61 idxd_select_device(struct idxd_io_channel *chan)
62 {
63 	uint32_t count = 0;
64 	struct idxd_device *dev;
65 	uint32_t socket_id = spdk_env_get_socket_id(spdk_env_get_current_core());
66 
67 	/*
68 	 * We allow channels to share underlying devices,
69 	 * selection is round-robin based with a limitation
70 	 * on how many channel can share one device.
71 	 */
72 	do {
73 		/* select next device */
74 		pthread_mutex_lock(&g_dev_lock);
75 		g_next_dev = TAILQ_NEXT(g_next_dev, tailq);
76 		if (g_next_dev == NULL) {
77 			g_next_dev = TAILQ_FIRST(&g_dsa_devices);
78 		}
79 		dev = g_next_dev;
80 		pthread_mutex_unlock(&g_dev_lock);
81 
82 		if (socket_id != spdk_idxd_get_socket(dev->dsa)) {
83 			continue;
84 		}
85 
86 		/*
87 		 * Now see if a channel is available on this one. We only
88 		 * allow a specific number of channels to share a device
89 		 * to limit outstanding IO for flow control purposes.
90 		 */
91 		chan->chan = spdk_idxd_get_channel(dev->dsa);
92 		if (chan->chan != NULL) {
93 			SPDK_DEBUGLOG(accel_dsa, "On socket %d using device on socket %d\n",
94 				      socket_id, spdk_idxd_get_socket(dev->dsa));
95 			return dev;
96 		}
97 	} while (++count < g_num_devices);
98 
99 	/* We are out of available channels and/or devices for the local socket. We fix the number
100 	 * of channels that we allocate per device and only allocate devices on the same socket
101 	 * that the current thread is on. If on a 2 socket system it may be possible to avoid
102 	 * this situation by spreading threads across the sockets.
103 	 */
104 	SPDK_ERRLOG("No more DSA devices available on the local socket.\n");
105 	return NULL;
106 }
107 
108 static void
109 dsa_done(void *cb_arg, int status)
110 {
111 	struct idxd_task *idxd_task = cb_arg;
112 	struct idxd_io_channel *chan;
113 
114 	chan = idxd_task->chan;
115 
116 	assert(chan->num_outstanding > 0);
117 	spdk_trace_record(TRACE_ACCEL_DSA_OP_COMPLETE, 0, 0, 0, chan->num_outstanding - 1);
118 	chan->num_outstanding--;
119 
120 	spdk_accel_task_complete(&idxd_task->task, status);
121 }
122 
123 static int
124 idxd_submit_dualcast(struct idxd_io_channel *ch, struct idxd_task *idxd_task, int flags)
125 {
126 	struct spdk_accel_task *task = &idxd_task->task;
127 
128 	if (spdk_unlikely(task->d.iovcnt != 1 || task->d2.iovcnt != 1 || task->s.iovcnt != 1)) {
129 		return -EINVAL;
130 	}
131 
132 	if (spdk_unlikely(task->d.iovs[0].iov_len != task->s.iovs[0].iov_len ||
133 			  task->d.iovs[0].iov_len != task->d2.iovs[0].iov_len)) {
134 		return -EINVAL;
135 	}
136 
137 	return spdk_idxd_submit_dualcast(ch->chan, task->d.iovs[0].iov_base,
138 					 task->d2.iovs[0].iov_base, task->s.iovs[0].iov_base,
139 					 task->d.iovs[0].iov_len, flags, dsa_done, idxd_task);
140 }
141 
142 static int
143 _process_single_task(struct spdk_io_channel *ch, struct spdk_accel_task *task)
144 {
145 	struct idxd_io_channel *chan = spdk_io_channel_get_ctx(ch);
146 	struct idxd_task *idxd_task;
147 	int rc = 0, flags = 0;
148 
149 	idxd_task = SPDK_CONTAINEROF(task, struct idxd_task, task);
150 	idxd_task->chan = chan;
151 
152 	switch (task->op_code) {
153 	case SPDK_ACCEL_OPC_COPY:
154 		rc = spdk_idxd_submit_copy(chan->chan, task->d.iovs, task->d.iovcnt,
155 					   task->s.iovs, task->s.iovcnt, flags, dsa_done, idxd_task);
156 		break;
157 	case SPDK_ACCEL_OPC_DUALCAST:
158 		rc = idxd_submit_dualcast(chan, idxd_task, flags);
159 		break;
160 	case SPDK_ACCEL_OPC_COMPARE:
161 		rc = spdk_idxd_submit_compare(chan->chan, task->s.iovs, task->s.iovcnt,
162 					      task->s2.iovs, task->s2.iovcnt, flags,
163 					      dsa_done, idxd_task);
164 		break;
165 	case SPDK_ACCEL_OPC_FILL:
166 		rc = spdk_idxd_submit_fill(chan->chan, task->d.iovs, task->d.iovcnt,
167 					   task->fill_pattern, flags, dsa_done, idxd_task);
168 		break;
169 	case SPDK_ACCEL_OPC_CRC32C:
170 		rc = spdk_idxd_submit_crc32c(chan->chan, task->s.iovs, task->s.iovcnt, task->seed,
171 					     task->crc_dst, flags, dsa_done, idxd_task);
172 		break;
173 	case SPDK_ACCEL_OPC_COPY_CRC32C:
174 		rc = spdk_idxd_submit_copy_crc32c(chan->chan, task->d.iovs, task->d.iovcnt,
175 						  task->s.iovs, task->s.iovcnt,
176 						  task->seed, task->crc_dst, flags,
177 						  dsa_done, idxd_task);
178 		break;
179 	default:
180 		assert(false);
181 		rc = -EINVAL;
182 		break;
183 	}
184 
185 	if (rc == 0) {
186 		chan->num_outstanding++;
187 		spdk_trace_record(TRACE_ACCEL_DSA_OP_SUBMIT, 0, 0, 0, chan->num_outstanding);
188 	}
189 
190 	return rc;
191 }
192 
193 static int
194 dsa_submit_task(struct spdk_io_channel *ch, struct spdk_accel_task *task)
195 {
196 	struct idxd_io_channel *chan = spdk_io_channel_get_ctx(ch);
197 	int rc = 0;
198 
199 	assert(TAILQ_NEXT(task, link) == NULL);
200 
201 	if (spdk_unlikely(chan->state == IDXD_CHANNEL_ERROR)) {
202 		spdk_accel_task_complete(task, -EINVAL);
203 		return 0;
204 	}
205 
206 	if (!TAILQ_EMPTY(&chan->queued_tasks)) {
207 		TAILQ_INSERT_TAIL(&chan->queued_tasks, task, link);
208 		return 0;
209 	}
210 
211 	rc = _process_single_task(ch, task);
212 	if (rc == -EBUSY) {
213 		TAILQ_INSERT_TAIL(&chan->queued_tasks, task, link);
214 	} else if (rc) {
215 		spdk_accel_task_complete(task, rc);
216 	}
217 
218 	return 0;
219 }
220 
221 static int
222 dsa_submit_queued_tasks(struct idxd_io_channel *chan)
223 {
224 	struct spdk_accel_task *task, *tmp;
225 	struct spdk_io_channel *ch = spdk_io_channel_from_ctx(chan);
226 	int rc = 0;
227 
228 	if (spdk_unlikely(chan->state == IDXD_CHANNEL_ERROR)) {
229 		/* Complete queued tasks with error and clear the list */
230 		while ((task = TAILQ_FIRST(&chan->queued_tasks))) {
231 			TAILQ_REMOVE(&chan->queued_tasks, task, link);
232 			spdk_accel_task_complete(task, -EINVAL);
233 		}
234 		return 0;
235 	}
236 
237 	TAILQ_FOREACH_SAFE(task, &chan->queued_tasks, link, tmp) {
238 		rc = _process_single_task(ch, task);
239 		if (rc == -EBUSY) {
240 			return rc;
241 		}
242 		TAILQ_REMOVE(&chan->queued_tasks, task, link);
243 		if (rc) {
244 			spdk_accel_task_complete(task, rc);
245 		}
246 	}
247 
248 	return 0;
249 }
250 
251 static int
252 idxd_poll(void *arg)
253 {
254 	struct idxd_io_channel *chan = arg;
255 	int count;
256 
257 	count = spdk_idxd_process_events(chan->chan);
258 
259 	/* Check if there are any pending ops to process if the channel is active */
260 	if (!TAILQ_EMPTY(&chan->queued_tasks)) {
261 		dsa_submit_queued_tasks(chan);
262 	}
263 
264 	return count > 0 ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE;
265 }
266 
267 static size_t
268 accel_dsa_get_ctx_size(void)
269 {
270 	return sizeof(struct idxd_task);
271 }
272 
273 static bool
274 dsa_supports_opcode(enum spdk_accel_opcode opc)
275 {
276 	if (!g_dsa_initialized) {
277 		assert(0);
278 		return false;
279 	}
280 
281 	switch (opc) {
282 	case SPDK_ACCEL_OPC_COPY:
283 	case SPDK_ACCEL_OPC_FILL:
284 	case SPDK_ACCEL_OPC_DUALCAST:
285 	case SPDK_ACCEL_OPC_COMPARE:
286 	case SPDK_ACCEL_OPC_CRC32C:
287 	case SPDK_ACCEL_OPC_COPY_CRC32C:
288 		return true;
289 	default:
290 		return false;
291 	}
292 }
293 
294 static int accel_dsa_init(void);
295 static void accel_dsa_exit(void *ctx);
296 static void accel_dsa_write_config_json(struct spdk_json_write_ctx *w);
297 
298 static struct spdk_accel_module_if g_dsa_module = {
299 	.module_init = accel_dsa_init,
300 	.module_fini = accel_dsa_exit,
301 	.write_config_json = accel_dsa_write_config_json,
302 	.get_ctx_size = accel_dsa_get_ctx_size,
303 	.name			= "dsa",
304 	.supports_opcode	= dsa_supports_opcode,
305 	.get_io_channel		= dsa_get_io_channel,
306 	.submit_tasks		= dsa_submit_task
307 };
308 
309 static int
310 dsa_create_cb(void *io_device, void *ctx_buf)
311 {
312 	struct idxd_io_channel *chan = ctx_buf;
313 	struct idxd_device *dsa;
314 
315 	dsa = idxd_select_device(chan);
316 	if (dsa == NULL) {
317 		SPDK_ERRLOG("Failed to get an idxd channel\n");
318 		return -EINVAL;
319 	}
320 
321 	chan->dev = dsa;
322 	chan->poller = SPDK_POLLER_REGISTER(idxd_poll, chan, 0);
323 	TAILQ_INIT(&chan->queued_tasks);
324 	chan->num_outstanding = 0;
325 	chan->state = IDXD_CHANNEL_ACTIVE;
326 
327 	return 0;
328 }
329 
330 static void
331 dsa_destroy_cb(void *io_device, void *ctx_buf)
332 {
333 	struct idxd_io_channel *chan = ctx_buf;
334 
335 	spdk_poller_unregister(&chan->poller);
336 	spdk_idxd_put_channel(chan->chan);
337 }
338 
339 static struct spdk_io_channel *
340 dsa_get_io_channel(void)
341 {
342 	return spdk_get_io_channel(&g_dsa_module);
343 }
344 
345 static void
346 attach_cb(void *cb_ctx, struct spdk_idxd_device *idxd)
347 {
348 	struct idxd_device *dev;
349 
350 	dev = calloc(1, sizeof(*dev));
351 	if (dev == NULL) {
352 		SPDK_ERRLOG("Failed to allocate device struct\n");
353 		return;
354 	}
355 
356 	dev->dsa = idxd;
357 	if (g_next_dev == NULL) {
358 		g_next_dev = dev;
359 	}
360 
361 	TAILQ_INSERT_TAIL(&g_dsa_devices, dev, tailq);
362 	g_num_devices++;
363 }
364 
365 int
366 accel_dsa_enable_probe(bool kernel_mode)
367 {
368 	int rc;
369 
370 	if (g_dsa_enable) {
371 		return -EALREADY;
372 	}
373 
374 	rc = spdk_idxd_set_config(kernel_mode);
375 	if (rc != 0) {
376 		return rc;
377 	}
378 
379 	spdk_accel_module_list_add(&g_dsa_module);
380 	g_kernel_mode = kernel_mode;
381 	g_dsa_enable = true;
382 
383 	return 0;
384 }
385 
386 static bool
387 probe_cb(void *cb_ctx, struct spdk_pci_device *dev)
388 {
389 	if (dev->id.device_id == PCI_DEVICE_ID_INTEL_DSA) {
390 		return true;
391 	}
392 
393 	return false;
394 }
395 
396 static int
397 accel_dsa_init(void)
398 {
399 	if (!g_dsa_enable) {
400 		return -EINVAL;
401 	}
402 
403 	if (spdk_idxd_probe(NULL, attach_cb, probe_cb) != 0) {
404 		SPDK_ERRLOG("spdk_idxd_probe() failed\n");
405 		return -EINVAL;
406 	}
407 
408 	if (TAILQ_EMPTY(&g_dsa_devices)) {
409 		SPDK_NOTICELOG("no available dsa devices\n");
410 		return -EINVAL;
411 	}
412 
413 	g_dsa_initialized = true;
414 	spdk_io_device_register(&g_dsa_module, dsa_create_cb, dsa_destroy_cb,
415 				sizeof(struct idxd_io_channel), "dsa_accel_module");
416 	return 0;
417 }
418 
419 static void
420 accel_dsa_exit(void *ctx)
421 {
422 	struct idxd_device *dev;
423 
424 	if (g_dsa_initialized) {
425 		spdk_io_device_unregister(&g_dsa_module, NULL);
426 		g_dsa_initialized = false;
427 	}
428 
429 	while (!TAILQ_EMPTY(&g_dsa_devices)) {
430 		dev = TAILQ_FIRST(&g_dsa_devices);
431 		TAILQ_REMOVE(&g_dsa_devices, dev, tailq);
432 		spdk_idxd_detach(dev->dsa);
433 		free(dev);
434 	}
435 
436 	spdk_accel_module_finish();
437 }
438 
439 static void
440 accel_dsa_write_config_json(struct spdk_json_write_ctx *w)
441 {
442 	if (g_dsa_enable) {
443 		spdk_json_write_object_begin(w);
444 		spdk_json_write_named_string(w, "method", "dsa_scan_accel_module");
445 		spdk_json_write_named_object_begin(w, "params");
446 		spdk_json_write_named_bool(w, "config_kernel_mode", g_kernel_mode);
447 		spdk_json_write_object_end(w);
448 		spdk_json_write_object_end(w);
449 	}
450 }
451 
452 SPDK_TRACE_REGISTER_FN(dsa_trace, "dsa", TRACE_GROUP_ACCEL_DSA)
453 {
454 	spdk_trace_register_description("DSA_OP_SUBMIT", TRACE_ACCEL_DSA_OP_SUBMIT, OWNER_NONE, OBJECT_NONE,
455 					0,
456 					SPDK_TRACE_ARG_TYPE_INT, "count");
457 	spdk_trace_register_description("DSA_OP_COMPLETE", TRACE_ACCEL_DSA_OP_COMPLETE, OWNER_NONE,
458 					OBJECT_NONE,
459 					0, SPDK_TRACE_ARG_TYPE_INT, "count");
460 }
461 
462 SPDK_LOG_REGISTER_COMPONENT(accel_dsa)
463