1 /* SPDX-License-Identifier: Apache-2.0
2 * Copyright(c) 2022 Cisco Systems, Inc.
3 * Copyright (c) 2022 Intel and/or its affiliates.
7 #include <vlib/pci/pci.h>
8 #include <vlib/dma/dma.h>
9 #include <vppinfra/heap.h>
10 #include <vppinfra/atomics.h>
11 #include <vnet/plugin/plugin.h>
12 #include <vpp/app/version.h>
13 #include <dma_intel/dsa_intel.h>
15 extern vlib_node_registration_t intel_dsa_node;
17 VLIB_REGISTER_LOG_CLASS (intel_dsa_log, static) = {
18 .class_name = "intel_dsa",
19 .subclass_name = "dsa",
23 intel_dsa_channel_lock (intel_dsa_channel_t *ch)
26 if (ch->n_threads < 2)
29 /* channel is used by multiple threads so we need to lock it */
30 while (!__atomic_compare_exchange_n (&ch->lock, &expected,
31 /* desired */ 1, /* weak */ 0,
32 __ATOMIC_ACQUIRE, __ATOMIC_RELAXED))
34 while (__atomic_load_n (&ch->lock, __ATOMIC_RELAXED))
41 intel_dsa_channel_unlock (intel_dsa_channel_t *ch)
43 if (ch->n_threads < 2)
46 __atomic_store_n (&ch->lock, 0, __ATOMIC_RELEASE);
49 static vlib_dma_batch_t *
50 intel_dsa_batch_new (vlib_main_t *vm, struct vlib_dma_config_data *cd)
52 intel_dsa_main_t *idm = &intel_dsa_main;
53 intel_dsa_config_t *idc;
56 idc = vec_elt_at_index (idm->dsa_config_heap,
57 cd->private_data + vm->thread_index);
59 if (vec_len (idc->freelist) > 0)
60 b = vec_pop (idc->freelist);
63 clib_spinlock_lock (&idm->lock);
64 b = vlib_physmem_alloc (vm, idc->alloc_size);
65 clib_spinlock_unlock (&idm->lock);
66 /* if no free space in physmem, force quit */
68 *b = idc->batch_template;
69 b->max_transfers = idc->max_transfers;
71 u32 def_flags = (INTEL_DSA_OP_MEMMOVE << INTEL_DSA_OP_SHIFT) |
72 INTEL_DSA_FLAG_CACHE_CONTROL;
73 if (b->ch->block_on_fault)
74 def_flags |= INTEL_DSA_FLAG_BLOCK_ON_FAULT;
75 for (int i = 0; i < idc->max_transfers; i++)
77 intel_dsa_desc_t *dsa_desc = b->descs + i;
78 dsa_desc->op_flags = def_flags;
85 #if defined(__x86_64__) || defined(i386)
86 static_always_inline void
87 __movdir64b (volatile void *dst, const void *src)
89 asm volatile(".byte 0x66, 0x0f, 0x38, 0xf8, 0x02"
96 static_always_inline void
97 intel_dsa_batch_fallback (vlib_main_t *vm, intel_dsa_batch_t *b,
98 intel_dsa_channel_t *ch)
100 for (u16 i = 0; i < b->batch.n_enq; i++)
102 intel_dsa_desc_t *desc = &b->descs[i];
103 clib_memcpy_fast (desc->dst, desc->src, desc->size);
105 b->status = INTEL_DSA_STATUS_CPU_SUCCESS;
111 intel_dsa_batch_submit (vlib_main_t *vm, struct vlib_dma_batch *vb)
113 intel_dsa_main_t *idm = &intel_dsa_main;
114 intel_dsa_batch_t *b = (intel_dsa_batch_t *) vb;
115 intel_dsa_channel_t *ch = b->ch;
116 if (PREDICT_FALSE (vb->n_enq == 0))
118 vec_add1 (idm->dsa_config_heap[b->config_heap_index].freelist, b);
122 intel_dsa_channel_lock (ch);
123 if (ch->n_enq >= ch->size)
127 intel_dsa_channel_unlock (ch);
130 /* skip channel limitation if first pending finished */
131 intel_dsa_batch_t *lb = NULL;
133 vec_len (idm->dsa_threads[vm->thread_index].pending_batches);
136 idm->dsa_threads[vm->thread_index].pending_batches[n_pendings - 1];
138 if (!lb || lb->status != INTEL_DSA_STATUS_SUCCESS)
140 intel_dsa_batch_fallback (vm, b, ch);
145 b->status = INTEL_DSA_STATUS_BUSY;
146 if (PREDICT_FALSE (vb->n_enq == 1))
148 intel_dsa_desc_t *desc = &b->descs[0];
149 desc->completion = (u64) &b->completion_cl;
150 desc->op_flags |= INTEL_DSA_FLAG_COMPLETION_ADDR_VALID |
151 INTEL_DSA_FLAG_REQUEST_COMPLETION;
152 #if defined(__x86_64__) || defined(i386)
153 _mm_sfence (); /* fence before writing desc to device */
154 __movdir64b (ch->portal, (void *) desc);
159 intel_dsa_desc_t *batch_desc = &b->descs[b->max_transfers];
160 batch_desc->op_flags = (INTEL_DSA_OP_BATCH << INTEL_DSA_OP_SHIFT) |
161 INTEL_DSA_FLAG_COMPLETION_ADDR_VALID |
162 INTEL_DSA_FLAG_REQUEST_COMPLETION;
163 batch_desc->desc_addr = (void *) (b->descs);
164 batch_desc->size = vb->n_enq;
165 batch_desc->completion = (u64) &b->completion_cl;
166 #if defined(__x86_64__) || defined(i386)
167 _mm_sfence (); /* fence before writing desc to device */
168 __movdir64b (ch->portal, (void *) batch_desc);
176 intel_dsa_channel_unlock (ch);
177 vec_add1 (idm->dsa_threads[vm->thread_index].pending_batches, b);
178 vlib_node_set_interrupt_pending (vm, intel_dsa_node.index);
183 intel_dsa_check_channel (intel_dsa_channel_t *ch, vlib_dma_config_data_t *cd)
187 dsa_log_error ("no available dsa channel");
190 vlib_dma_config_t supported_cfg = {
191 .barrier_before_last = 1,
195 if (cd->cfg.features & ~supported_cfg.features)
197 dsa_log_error ("unsupported feature requested");
201 if (cd->cfg.max_transfers > ch->max_transfers)
203 dsa_log_error ("transfer number (%u) too big", cd->cfg.max_transfers);
207 if (cd->cfg.max_transfer_size > ch->max_transfer_size)
209 dsa_log_error ("transfer size (%u) too big", cd->cfg.max_transfer_size);
215 static_always_inline void
216 intel_dsa_alloc_dma_batch (vlib_main_t *vm, intel_dsa_config_t *idc)
218 intel_dsa_batch_t *b;
219 b = vlib_physmem_alloc (vm, idc->alloc_size);
220 /* if no free space in physmem, force quit */
222 *b = idc->batch_template;
223 b->max_transfers = idc->max_transfers;
225 u32 def_flags = (INTEL_DSA_OP_MEMMOVE << INTEL_DSA_OP_SHIFT) |
226 INTEL_DSA_FLAG_CACHE_CONTROL;
227 if (b->ch->block_on_fault)
228 def_flags |= INTEL_DSA_FLAG_BLOCK_ON_FAULT;
230 for (int i = 0; i < idc->max_transfers; i++)
232 intel_dsa_desc_t *dsa_desc = b->descs + i;
233 dsa_desc->op_flags = def_flags;
235 vec_add1 (idc->freelist, b);
239 intel_dsa_config_add_fn (vlib_main_t *vm, vlib_dma_config_data_t *cd)
241 intel_dsa_main_t *idm = &intel_dsa_main;
242 intel_dsa_config_t *idc;
243 u32 index, n_threads = vlib_get_n_threads ();
245 vec_validate (idm->dsa_config_heap_handle_by_config_index, cd->config_index);
246 index = heap_alloc_aligned (
247 idm->dsa_config_heap, n_threads, CLIB_CACHE_LINE_BYTES,
248 idm->dsa_config_heap_handle_by_config_index[cd->config_index]);
250 cd->batch_new_fn = intel_dsa_batch_new;
251 cd->private_data = index;
253 for (u32 thread = 0; thread < n_threads; thread++)
255 intel_dsa_batch_t *idb;
257 idc = vec_elt_at_index (idm->dsa_config_heap, index + thread);
259 /* size of physmem allocation for this config */
260 idc->max_transfers = cd->cfg.max_transfers;
261 idc->alloc_size = sizeof (intel_dsa_batch_t) +
262 sizeof (intel_dsa_desc_t) * (idc->max_transfers + 1);
263 /* fill batch template */
264 idb = &idc->batch_template;
265 idb->ch = idm->dsa_threads[thread].ch;
266 if (intel_dsa_check_channel (idb->ch, cd))
269 dsa_log_debug ("config %d in thread %d using channel %u/%u",
270 cd->config_index, thread, idb->ch->did, idb->ch->qid);
271 idb->config_heap_index = index + thread;
272 idb->config_index = cd->config_index;
273 idb->batch.callback_fn = cd->cfg.callback_fn;
274 idb->features = cd->cfg.features;
276 b->stride = sizeof (intel_dsa_desc_t);
277 b->src_ptr_off = STRUCT_OFFSET_OF (intel_dsa_batch_t, descs[0].src);
278 b->dst_ptr_off = STRUCT_OFFSET_OF (intel_dsa_batch_t, descs[0].dst);
279 b->size_off = STRUCT_OFFSET_OF (intel_dsa_batch_t, descs[0].size);
280 b->submit_fn = intel_dsa_batch_submit;
282 "config %d in thread %d stride %d src/dst/size offset %d-%d-%d",
283 cd->config_index, thread, b->stride, b->src_ptr_off, b->dst_ptr_off,
286 /* allocate dma batch in advance */
287 for (u32 index = 0; index < cd->cfg.max_batches; index++)
288 intel_dsa_alloc_dma_batch (vm, idc);
291 dsa_log_info ("config %u added", cd->private_data);
297 intel_dsa_config_del_fn (vlib_main_t *vm, vlib_dma_config_data_t *cd)
299 intel_dsa_main_t *idm = &intel_dsa_main;
300 intel_dsa_thread_t *t =
301 vec_elt_at_index (idm->dsa_threads, vm->thread_index);
302 u32 n_pending, n_threads, config_heap_index, n = 0;
303 n_threads = vlib_get_n_threads ();
305 if (!t->pending_batches)
308 n_pending = vec_len (t->pending_batches);
309 intel_dsa_batch_t *b;
311 /* clean pending list and free list */
312 for (u32 i = 0; i < n_pending; i++)
314 b = t->pending_batches[i];
315 if (b->config_index == cd->config_index)
317 vec_add1 (idm->dsa_config_heap[b->config_heap_index].freelist, b);
318 if (b->status == INTEL_DSA_STATUS_SUCCESS ||
319 b->status == INTEL_DSA_STATUS_BUSY)
323 t->pending_batches[n++] = b;
326 vec_set_len (t->pending_batches, n);
329 for (u32 thread = 0; thread < n_threads; thread++)
331 config_heap_index = cd->private_data + thread;
332 while (vec_len (idm->dsa_config_heap[config_heap_index].freelist) > 0)
334 b = vec_pop (idm->dsa_config_heap[config_heap_index].freelist);
335 vlib_physmem_free (vm, b);
339 heap_dealloc (idm->dsa_config_heap,
340 idm->dsa_config_heap_handle_by_config_index[cd->config_index]);
342 dsa_log_debug ("config %u removed", cd->private_data);
346 intel_dsa_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
349 intel_dsa_main_t *idm = &intel_dsa_main;
350 intel_dsa_thread_t *t =
351 vec_elt_at_index (idm->dsa_threads, vm->thread_index);
352 u32 n_pending = 0, n = 0;
353 u8 glitch = 0, status;
355 if (!t->pending_batches)
358 n_pending = vec_len (t->pending_batches);
360 for (u32 i = 0; i < n_pending; i++)
362 intel_dsa_batch_t *b = t->pending_batches[i];
363 intel_dsa_channel_t *ch = b->ch;
366 if ((status == INTEL_DSA_STATUS_SUCCESS ||
367 status == INTEL_DSA_STATUS_CPU_SUCCESS) &&
371 if (b->batch.callback_fn)
372 b->batch.callback_fn (vm, &b->batch);
374 /* restore last descriptor fields */
375 if (b->batch.n_enq == 1)
377 b->descs[0].completion = 0;
378 b->descs[0].op_flags =
379 (INTEL_DSA_OP_MEMMOVE << INTEL_DSA_OP_SHIFT) |
380 INTEL_DSA_FLAG_CACHE_CONTROL;
381 if (b->ch->block_on_fault)
382 b->descs[0].op_flags |= INTEL_DSA_FLAG_BLOCK_ON_FAULT;
384 /* add to freelist */
385 vec_add1 (idm->dsa_config_heap[b->config_heap_index].freelist, b);
387 intel_dsa_channel_lock (ch);
388 if (status == INTEL_DSA_STATUS_SUCCESS)
395 intel_dsa_channel_unlock (ch);
398 b->status = INTEL_DSA_STATUS_IDLE;
400 else if (status == INTEL_DSA_STATUS_BUSY)
402 glitch = 1 & b->barrier_before_last;
403 t->pending_batches[n++] = b;
407 /* fallback to software if exception happened */
408 intel_dsa_batch_fallback (vm, b, ch);
409 glitch = 1 & b->barrier_before_last;
413 t->pending_batches[n++] = b;
416 vec_set_len (t->pending_batches, n);
420 vlib_node_set_interrupt_pending (vm, intel_dsa_node.index);
423 return n_pending - n;
427 format_dsa_info (u8 *s, va_list *args)
429 intel_dsa_main_t *idm = &intel_dsa_main;
430 vlib_main_t *vm = va_arg (*args, vlib_main_t *);
431 intel_dsa_channel_t *ch;
432 ch = idm->dsa_threads[vm->thread_index].ch;
433 s = format (s, "thread %d dma %u/%u request %-16lld hw %-16lld cpu %-16lld",
434 vm->thread_index, ch->did, ch->qid, ch->submitted, ch->completed,
439 VLIB_REGISTER_NODE (intel_dsa_node) = {
440 .function = intel_dsa_node_fn,
442 .type = VLIB_NODE_TYPE_INPUT,
443 .state = VLIB_NODE_STATE_INTERRUPT,
447 vlib_dma_backend_t intel_dsa_backend = {
449 .config_add_fn = intel_dsa_config_add_fn,
450 .config_del_fn = intel_dsa_config_del_fn,
451 .info_fn = format_dsa_info,