xref: /spdk/module/accel/dsa/accel_dsa.c (revision b02581a89058ebaebe03bd0e16e3b58adfe406c1)
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 	STAILQ_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 	int rc;
114 
115 	chan = idxd_task->chan;
116 
117 	/* If the DSA DIF Check operation detects an error, detailed info about
118 	 * this error (like actual/expected values) needs to be obtained by
119 	 * calling the software DIF Verify operation.
120 	 */
121 	if (spdk_unlikely(status == -EIO)) {
122 		if (idxd_task->task.op_code == SPDK_ACCEL_OPC_DIF_VERIFY) {
123 			rc = spdk_dif_verify(idxd_task->task.s.iovs, idxd_task->task.s.iovcnt,
124 					     idxd_task->task.dif.num_blocks,
125 					     idxd_task->task.dif.ctx, idxd_task->task.dif.err);
126 			if (rc != 0) {
127 				SPDK_ERRLOG("DIF error detected. type=%d, offset=%" PRIu32 "\n",
128 					    idxd_task->task.dif.err->err_type,
129 					    idxd_task->task.dif.err->err_offset);
130 			}
131 		}
132 	}
133 
134 	assert(chan->num_outstanding > 0);
135 	spdk_trace_record(TRACE_ACCEL_DSA_OP_COMPLETE, 0, 0, 0, chan->num_outstanding - 1);
136 	chan->num_outstanding--;
137 
138 	spdk_accel_task_complete(&idxd_task->task, status);
139 }
140 
141 static int
142 idxd_submit_dualcast(struct idxd_io_channel *ch, struct idxd_task *idxd_task, int flags)
143 {
144 	struct spdk_accel_task *task = &idxd_task->task;
145 
146 	if (spdk_unlikely(task->d.iovcnt != 1 || task->d2.iovcnt != 1 || task->s.iovcnt != 1)) {
147 		return -EINVAL;
148 	}
149 
150 	if (spdk_unlikely(task->d.iovs[0].iov_len != task->s.iovs[0].iov_len ||
151 			  task->d.iovs[0].iov_len != task->d2.iovs[0].iov_len)) {
152 		return -EINVAL;
153 	}
154 
155 	return spdk_idxd_submit_dualcast(ch->chan, task->d.iovs[0].iov_base,
156 					 task->d2.iovs[0].iov_base, task->s.iovs[0].iov_base,
157 					 task->d.iovs[0].iov_len, flags, dsa_done, idxd_task);
158 }
159 
160 static int
161 _process_single_task(struct spdk_io_channel *ch, struct spdk_accel_task *task)
162 {
163 	struct idxd_io_channel *chan = spdk_io_channel_get_ctx(ch);
164 	struct idxd_task *idxd_task;
165 	int rc = 0, flags = 0;
166 
167 	idxd_task = SPDK_CONTAINEROF(task, struct idxd_task, task);
168 	idxd_task->chan = chan;
169 
170 	switch (task->op_code) {
171 	case SPDK_ACCEL_OPC_COPY:
172 		rc = spdk_idxd_submit_copy(chan->chan, task->d.iovs, task->d.iovcnt,
173 					   task->s.iovs, task->s.iovcnt, flags, dsa_done, idxd_task);
174 		break;
175 	case SPDK_ACCEL_OPC_DUALCAST:
176 		rc = idxd_submit_dualcast(chan, idxd_task, flags);
177 		break;
178 	case SPDK_ACCEL_OPC_COMPARE:
179 		rc = spdk_idxd_submit_compare(chan->chan, task->s.iovs, task->s.iovcnt,
180 					      task->s2.iovs, task->s2.iovcnt, flags,
181 					      dsa_done, idxd_task);
182 		break;
183 	case SPDK_ACCEL_OPC_FILL:
184 		rc = spdk_idxd_submit_fill(chan->chan, task->d.iovs, task->d.iovcnt,
185 					   task->fill_pattern, flags, dsa_done, idxd_task);
186 		break;
187 	case SPDK_ACCEL_OPC_CRC32C:
188 		rc = spdk_idxd_submit_crc32c(chan->chan, task->s.iovs, task->s.iovcnt, task->seed,
189 					     task->crc_dst, flags, dsa_done, idxd_task);
190 		break;
191 	case SPDK_ACCEL_OPC_COPY_CRC32C:
192 		rc = spdk_idxd_submit_copy_crc32c(chan->chan, task->d.iovs, task->d.iovcnt,
193 						  task->s.iovs, task->s.iovcnt,
194 						  task->seed, task->crc_dst, flags,
195 						  dsa_done, idxd_task);
196 		break;
197 	case SPDK_ACCEL_OPC_DIF_VERIFY:
198 		rc = spdk_idxd_submit_dif_check(chan->chan,
199 						task->s.iovs, task->s.iovcnt,
200 						task->dif.num_blocks, task->dif.ctx, flags,
201 						dsa_done, idxd_task);
202 		break;
203 	case SPDK_ACCEL_OPC_DIF_GENERATE_COPY:
204 		rc = spdk_idxd_submit_dif_insert(chan->chan,
205 						 task->d.iovs, task->d.iovcnt,
206 						 task->s.iovs, task->s.iovcnt,
207 						 task->dif.num_blocks, task->dif.ctx, flags,
208 						 dsa_done, idxd_task);
209 		break;
210 	default:
211 		assert(false);
212 		rc = -EINVAL;
213 		break;
214 	}
215 
216 	if (rc == 0) {
217 		chan->num_outstanding++;
218 		spdk_trace_record(TRACE_ACCEL_DSA_OP_SUBMIT, 0, 0, 0, chan->num_outstanding);
219 	}
220 
221 	return rc;
222 }
223 
224 static int
225 dsa_submit_task(struct spdk_io_channel *ch, struct spdk_accel_task *task)
226 {
227 	struct idxd_io_channel *chan = spdk_io_channel_get_ctx(ch);
228 	int rc = 0;
229 
230 	assert(STAILQ_NEXT(task, link) == NULL);
231 
232 	if (spdk_unlikely(chan->state == IDXD_CHANNEL_ERROR)) {
233 		spdk_accel_task_complete(task, -EINVAL);
234 		return 0;
235 	}
236 
237 	if (!STAILQ_EMPTY(&chan->queued_tasks)) {
238 		STAILQ_INSERT_TAIL(&chan->queued_tasks, task, link);
239 		return 0;
240 	}
241 
242 	rc = _process_single_task(ch, task);
243 	if (rc == -EBUSY) {
244 		STAILQ_INSERT_TAIL(&chan->queued_tasks, task, link);
245 	} else if (rc) {
246 		spdk_accel_task_complete(task, rc);
247 	}
248 
249 	return 0;
250 }
251 
252 static int
253 dsa_submit_queued_tasks(struct idxd_io_channel *chan)
254 {
255 	struct spdk_accel_task *task, *tmp;
256 	struct spdk_io_channel *ch = spdk_io_channel_from_ctx(chan);
257 	int rc = 0;
258 
259 	if (spdk_unlikely(chan->state == IDXD_CHANNEL_ERROR)) {
260 		/* Complete queued tasks with error and clear the list */
261 		while ((task = STAILQ_FIRST(&chan->queued_tasks))) {
262 			STAILQ_REMOVE_HEAD(&chan->queued_tasks, link);
263 			spdk_accel_task_complete(task, -EINVAL);
264 		}
265 		return 0;
266 	}
267 
268 	STAILQ_FOREACH_SAFE(task, &chan->queued_tasks, link, tmp) {
269 		rc = _process_single_task(ch, task);
270 		if (rc == -EBUSY) {
271 			return rc;
272 		}
273 		STAILQ_REMOVE_HEAD(&chan->queued_tasks, link);
274 		if (rc) {
275 			spdk_accel_task_complete(task, rc);
276 		}
277 	}
278 
279 	return 0;
280 }
281 
282 static int
283 idxd_poll(void *arg)
284 {
285 	struct idxd_io_channel *chan = arg;
286 	int count;
287 
288 	count = spdk_idxd_process_events(chan->chan);
289 
290 	/* Check if there are any pending ops to process if the channel is active */
291 	if (!STAILQ_EMPTY(&chan->queued_tasks)) {
292 		dsa_submit_queued_tasks(chan);
293 	}
294 
295 	return count > 0 ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE;
296 }
297 
298 static size_t
299 accel_dsa_get_ctx_size(void)
300 {
301 	return sizeof(struct idxd_task);
302 }
303 
304 static bool
305 dsa_supports_opcode(enum spdk_accel_opcode opc)
306 {
307 	if (!g_dsa_initialized) {
308 		assert(0);
309 		return false;
310 	}
311 
312 	switch (opc) {
313 	case SPDK_ACCEL_OPC_COPY:
314 	case SPDK_ACCEL_OPC_FILL:
315 	case SPDK_ACCEL_OPC_DUALCAST:
316 	case SPDK_ACCEL_OPC_COMPARE:
317 	case SPDK_ACCEL_OPC_CRC32C:
318 	case SPDK_ACCEL_OPC_COPY_CRC32C:
319 		return true;
320 	case SPDK_ACCEL_OPC_DIF_VERIFY:
321 	case SPDK_ACCEL_OPC_DIF_GENERATE_COPY:
322 		/* Supported only if the IOMMU is enabled */
323 		return spdk_iommu_is_enabled();
324 	default:
325 		return false;
326 	}
327 }
328 
329 static int accel_dsa_init(void);
330 static void accel_dsa_exit(void *ctx);
331 static void accel_dsa_write_config_json(struct spdk_json_write_ctx *w);
332 
333 static struct spdk_accel_module_if g_dsa_module = {
334 	.module_init = accel_dsa_init,
335 	.module_fini = accel_dsa_exit,
336 	.write_config_json = accel_dsa_write_config_json,
337 	.get_ctx_size = accel_dsa_get_ctx_size,
338 	.name			= "dsa",
339 	.supports_opcode	= dsa_supports_opcode,
340 	.get_io_channel		= dsa_get_io_channel,
341 	.submit_tasks		= dsa_submit_task
342 };
343 
344 static int
345 dsa_create_cb(void *io_device, void *ctx_buf)
346 {
347 	struct idxd_io_channel *chan = ctx_buf;
348 	struct idxd_device *dsa;
349 
350 	dsa = idxd_select_device(chan);
351 	if (dsa == NULL) {
352 		SPDK_ERRLOG("Failed to get an idxd channel\n");
353 		return -EINVAL;
354 	}
355 
356 	chan->dev = dsa;
357 	chan->poller = SPDK_POLLER_REGISTER(idxd_poll, chan, 0);
358 	STAILQ_INIT(&chan->queued_tasks);
359 	chan->num_outstanding = 0;
360 	chan->state = IDXD_CHANNEL_ACTIVE;
361 
362 	return 0;
363 }
364 
365 static void
366 dsa_destroy_cb(void *io_device, void *ctx_buf)
367 {
368 	struct idxd_io_channel *chan = ctx_buf;
369 
370 	spdk_poller_unregister(&chan->poller);
371 	spdk_idxd_put_channel(chan->chan);
372 }
373 
374 static struct spdk_io_channel *
375 dsa_get_io_channel(void)
376 {
377 	return spdk_get_io_channel(&g_dsa_module);
378 }
379 
380 static void
381 attach_cb(void *cb_ctx, struct spdk_idxd_device *idxd)
382 {
383 	struct idxd_device *dev;
384 
385 	dev = calloc(1, sizeof(*dev));
386 	if (dev == NULL) {
387 		SPDK_ERRLOG("Failed to allocate device struct\n");
388 		return;
389 	}
390 
391 	dev->dsa = idxd;
392 	if (g_next_dev == NULL) {
393 		g_next_dev = dev;
394 	}
395 
396 	TAILQ_INSERT_TAIL(&g_dsa_devices, dev, tailq);
397 	g_num_devices++;
398 }
399 
400 int
401 accel_dsa_enable_probe(bool kernel_mode)
402 {
403 	int rc;
404 
405 	if (g_dsa_enable) {
406 		return -EALREADY;
407 	}
408 
409 	rc = spdk_idxd_set_config(kernel_mode);
410 	if (rc != 0) {
411 		return rc;
412 	}
413 
414 	spdk_accel_module_list_add(&g_dsa_module);
415 	g_kernel_mode = kernel_mode;
416 	g_dsa_enable = true;
417 
418 	return 0;
419 }
420 
421 static bool
422 probe_cb(void *cb_ctx, struct spdk_pci_device *dev)
423 {
424 	if (dev->id.device_id == PCI_DEVICE_ID_INTEL_DSA) {
425 		return true;
426 	}
427 
428 	return false;
429 }
430 
431 static int
432 accel_dsa_init(void)
433 {
434 	if (!g_dsa_enable) {
435 		return -EINVAL;
436 	}
437 
438 	if (spdk_idxd_probe(NULL, attach_cb, probe_cb) != 0) {
439 		SPDK_ERRLOG("spdk_idxd_probe() failed\n");
440 		return -EINVAL;
441 	}
442 
443 	if (TAILQ_EMPTY(&g_dsa_devices)) {
444 		SPDK_NOTICELOG("no available dsa devices\n");
445 		return -EINVAL;
446 	}
447 
448 	g_dsa_initialized = true;
449 	spdk_io_device_register(&g_dsa_module, dsa_create_cb, dsa_destroy_cb,
450 				sizeof(struct idxd_io_channel), "dsa_accel_module");
451 	return 0;
452 }
453 
454 static void
455 accel_dsa_exit(void *ctx)
456 {
457 	struct idxd_device *dev;
458 
459 	if (g_dsa_initialized) {
460 		spdk_io_device_unregister(&g_dsa_module, NULL);
461 		g_dsa_initialized = false;
462 	}
463 
464 	while (!TAILQ_EMPTY(&g_dsa_devices)) {
465 		dev = TAILQ_FIRST(&g_dsa_devices);
466 		TAILQ_REMOVE(&g_dsa_devices, dev, tailq);
467 		spdk_idxd_detach(dev->dsa);
468 		free(dev);
469 	}
470 
471 	spdk_accel_module_finish();
472 }
473 
474 static void
475 accel_dsa_write_config_json(struct spdk_json_write_ctx *w)
476 {
477 	if (g_dsa_enable) {
478 		spdk_json_write_object_begin(w);
479 		spdk_json_write_named_string(w, "method", "dsa_scan_accel_module");
480 		spdk_json_write_named_object_begin(w, "params");
481 		spdk_json_write_named_bool(w, "config_kernel_mode", g_kernel_mode);
482 		spdk_json_write_object_end(w);
483 		spdk_json_write_object_end(w);
484 	}
485 }
486 
487 SPDK_TRACE_REGISTER_FN(dsa_trace, "dsa", TRACE_GROUP_ACCEL_DSA)
488 {
489 	spdk_trace_register_description("DSA_OP_SUBMIT", TRACE_ACCEL_DSA_OP_SUBMIT, OWNER_NONE, OBJECT_NONE,
490 					0,
491 					SPDK_TRACE_ARG_TYPE_INT, "count");
492 	spdk_trace_register_description("DSA_OP_COMPLETE", TRACE_ACCEL_DSA_OP_COMPLETE, OWNER_NONE,
493 					OBJECT_NONE,
494 					0, SPDK_TRACE_ARG_TYPE_INT, "count");
495 }
496 
497 SPDK_LOG_REGISTER_COMPONENT(accel_dsa)
498