nat: Include platform specific headers on FreeBSD
[vpp.git] / src / plugins / dma_intel / dsa.c
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright(c) 2022 Cisco Systems, Inc.
3  * Copyright (c) 2022 Intel and/or its affiliates.
4  */
5
6 #include <vlib/vlib.h>
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>
14
15 extern vlib_node_registration_t intel_dsa_node;
16
17 VLIB_REGISTER_LOG_CLASS (intel_dsa_log, static) = {
18   .class_name = "intel_dsa",
19   .subclass_name = "dsa",
20 };
21
22 static void
23 intel_dsa_channel_lock (intel_dsa_channel_t *ch)
24 {
25   u8 expected = 0;
26   if (ch->n_threads < 2)
27     return;
28
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))
33     {
34       while (__atomic_load_n (&ch->lock, __ATOMIC_RELAXED))
35         CLIB_PAUSE ();
36       expected = 0;
37     }
38 }
39
40 static void
41 intel_dsa_channel_unlock (intel_dsa_channel_t *ch)
42 {
43   if (ch->n_threads < 2)
44     return;
45
46   __atomic_store_n (&ch->lock, 0, __ATOMIC_RELEASE);
47 }
48
49 static vlib_dma_batch_t *
50 intel_dsa_batch_new (vlib_main_t *vm, struct vlib_dma_config_data *cd)
51 {
52   intel_dsa_main_t *idm = &intel_dsa_main;
53   intel_dsa_config_t *idc;
54   intel_dsa_batch_t *b;
55
56   idc = vec_elt_at_index (idm->dsa_config_heap,
57                           cd->private_data + vm->thread_index);
58
59   if (vec_len (idc->freelist) > 0)
60     b = vec_pop (idc->freelist);
61   else
62     {
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 */
67       ASSERT (b != NULL);
68       *b = idc->batch_template;
69       b->max_transfers = idc->max_transfers;
70
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++)
76         {
77           intel_dsa_desc_t *dsa_desc = b->descs + i;
78           dsa_desc->op_flags = def_flags;
79         }
80     }
81
82   return &b->batch;
83 }
84
85 #if defined(__x86_64__) || defined(i386)
86 static_always_inline void
87 __movdir64b (volatile void *dst, const void *src)
88 {
89   asm volatile(".byte 0x66, 0x0f, 0x38, 0xf8, 0x02"
90                :
91                : "a"(dst), "d"(src)
92                : "memory");
93 }
94 #endif
95
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)
99 {
100   for (u16 i = 0; i < b->batch.n_enq; i++)
101     {
102       intel_dsa_desc_t *desc = &b->descs[i];
103       clib_memcpy_fast (desc->dst, desc->src, desc->size);
104     }
105   b->status = INTEL_DSA_STATUS_CPU_SUCCESS;
106   ch->submitted++;
107   return;
108 }
109
110 int
111 intel_dsa_batch_submit (vlib_main_t *vm, struct vlib_dma_batch *vb)
112 {
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))
117     {
118       vec_add1 (idm->dsa_config_heap[b->config_heap_index].freelist, b);
119       return 0;
120     }
121
122   intel_dsa_channel_lock (ch);
123   if (ch->n_enq >= ch->size)
124     {
125       if (!b->sw_fallback)
126         {
127           intel_dsa_channel_unlock (ch);
128           return 0;
129         }
130       /* skip channel limitation if first pending finished */
131       intel_dsa_batch_t *lb = NULL;
132       u32 n_pendings =
133         vec_len (idm->dsa_threads[vm->thread_index].pending_batches);
134       if (n_pendings)
135         lb =
136           idm->dsa_threads[vm->thread_index].pending_batches[n_pendings - 1];
137
138       if (!lb || lb->status != INTEL_DSA_STATUS_SUCCESS)
139         {
140           intel_dsa_batch_fallback (vm, b, ch);
141           goto done;
142         }
143     }
144
145   b->status = INTEL_DSA_STATUS_BUSY;
146   if (PREDICT_FALSE (vb->n_enq == 1))
147     {
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);
155 #endif
156     }
157   else
158     {
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);
169 #endif
170     }
171
172   ch->submitted++;
173   ch->n_enq++;
174
175 done:
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);
179   return 1;
180 }
181
182 static int
183 intel_dsa_check_channel (intel_dsa_channel_t *ch, vlib_dma_config_data_t *cd)
184 {
185   if (!ch)
186     {
187       dsa_log_error ("no available dsa channel");
188       return 1;
189     }
190   vlib_dma_config_t supported_cfg = {
191     .barrier_before_last = 1,
192     .sw_fallback = 1,
193   };
194
195   if (cd->cfg.features & ~supported_cfg.features)
196     {
197       dsa_log_error ("unsupported feature requested");
198       return 1;
199     }
200
201   if (cd->cfg.max_transfers > ch->max_transfers)
202     {
203       dsa_log_error ("transfer number (%u) too big", cd->cfg.max_transfers);
204       return 1;
205     }
206
207   if (cd->cfg.max_transfer_size > ch->max_transfer_size)
208     {
209       dsa_log_error ("transfer size (%u) too big", cd->cfg.max_transfer_size);
210       return 1;
211     }
212   return 0;
213 }
214
215 static_always_inline void
216 intel_dsa_alloc_dma_batch (vlib_main_t *vm, intel_dsa_config_t *idc)
217 {
218   intel_dsa_batch_t *b;
219   b = vlib_physmem_alloc (vm, idc->alloc_size);
220   /* if no free space in physmem, force quit */
221   ASSERT (b != NULL);
222   *b = idc->batch_template;
223   b->max_transfers = idc->max_transfers;
224
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;
229
230   for (int i = 0; i < idc->max_transfers; i++)
231     {
232       intel_dsa_desc_t *dsa_desc = b->descs + i;
233       dsa_desc->op_flags = def_flags;
234     }
235   vec_add1 (idc->freelist, b);
236 }
237
238 static int
239 intel_dsa_config_add_fn (vlib_main_t *vm, vlib_dma_config_data_t *cd)
240 {
241   intel_dsa_main_t *idm = &intel_dsa_main;
242   intel_dsa_config_t *idc;
243   u32 index, n_threads = vlib_get_n_threads ();
244
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]);
249
250   cd->batch_new_fn = intel_dsa_batch_new;
251   cd->private_data = index;
252
253   for (u32 thread = 0; thread < n_threads; thread++)
254     {
255       intel_dsa_batch_t *idb;
256       vlib_dma_batch_t *b;
257       idc = vec_elt_at_index (idm->dsa_config_heap, index + thread);
258
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))
267         return 0;
268
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;
275       b = &idb->batch;
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;
281       dsa_log_debug (
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,
284         b->size_off);
285
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);
289     }
290
291   dsa_log_info ("config %u added", cd->private_data);
292
293   return 1;
294 }
295
296 static void
297 intel_dsa_config_del_fn (vlib_main_t *vm, vlib_dma_config_data_t *cd)
298 {
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 ();
304
305   if (!t->pending_batches)
306     goto free_heap;
307
308   n_pending = vec_len (t->pending_batches);
309   intel_dsa_batch_t *b;
310
311   /* clean pending list and free list */
312   for (u32 i = 0; i < n_pending; i++)
313     {
314       b = t->pending_batches[i];
315       if (b->config_index == cd->config_index)
316         {
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)
320             b->ch->n_enq--;
321         }
322       else
323         t->pending_batches[n++] = b;
324     }
325
326   vec_set_len (t->pending_batches, n);
327
328 free_heap:
329   for (u32 thread = 0; thread < n_threads; thread++)
330     {
331       config_heap_index = cd->private_data + thread;
332       while (vec_len (idm->dsa_config_heap[config_heap_index].freelist) > 0)
333         {
334           b = vec_pop (idm->dsa_config_heap[config_heap_index].freelist);
335           vlib_physmem_free (vm, b);
336         }
337     }
338
339   heap_dealloc (idm->dsa_config_heap,
340                 idm->dsa_config_heap_handle_by_config_index[cd->config_index]);
341
342   dsa_log_debug ("config %u removed", cd->private_data);
343 }
344
345 static uword
346 intel_dsa_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node,
347                    vlib_frame_t *frame)
348 {
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;
354
355   if (!t->pending_batches)
356     return 0;
357
358   n_pending = vec_len (t->pending_batches);
359
360   for (u32 i = 0; i < n_pending; i++)
361     {
362       intel_dsa_batch_t *b = t->pending_batches[i];
363       intel_dsa_channel_t *ch = b->ch;
364
365       status = b->status;
366       if ((status == INTEL_DSA_STATUS_SUCCESS ||
367            status == INTEL_DSA_STATUS_CPU_SUCCESS) &&
368           !glitch)
369         {
370           /* callback */
371           if (b->batch.callback_fn)
372             b->batch.callback_fn (vm, &b->batch);
373
374           /* restore last descriptor fields */
375           if (b->batch.n_enq == 1)
376             {
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;
383             }
384           /* add to freelist */
385           vec_add1 (idm->dsa_config_heap[b->config_heap_index].freelist, b);
386
387           intel_dsa_channel_lock (ch);
388           if (status == INTEL_DSA_STATUS_SUCCESS)
389             {
390               ch->n_enq--;
391               ch->completed++;
392             }
393           else
394             ch->sw_fallback++;
395           intel_dsa_channel_unlock (ch);
396
397           b->batch.n_enq = 0;
398           b->status = INTEL_DSA_STATUS_IDLE;
399         }
400       else if (status == INTEL_DSA_STATUS_BUSY)
401         {
402           glitch = 1 & b->barrier_before_last;
403           t->pending_batches[n++] = b;
404         }
405       else if (!glitch)
406         {
407           /* fallback to software if exception happened */
408           intel_dsa_batch_fallback (vm, b, ch);
409           glitch = 1 & b->barrier_before_last;
410         }
411       else
412         {
413           t->pending_batches[n++] = b;
414         }
415     }
416   vec_set_len (t->pending_batches, n);
417
418   if (n)
419     {
420       vlib_node_set_interrupt_pending (vm, intel_dsa_node.index);
421     }
422
423   return n_pending - n;
424 }
425
426 u8 *
427 format_dsa_info (u8 *s, va_list *args)
428 {
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,
435               ch->sw_fallback);
436   return s;
437 }
438
439 VLIB_REGISTER_NODE (intel_dsa_node) = {
440   .function = intel_dsa_node_fn,
441   .name = "intel-dsa",
442   .type = VLIB_NODE_TYPE_INPUT,
443   .state = VLIB_NODE_STATE_INTERRUPT,
444   .vector_size = 4,
445 };
446
447 vlib_dma_backend_t intel_dsa_backend = {
448   .name = "Intel DSA",
449   .config_add_fn = intel_dsa_config_add_fn,
450   .config_del_fn = intel_dsa_config_del_fn,
451   .info_fn = format_dsa_info,
452 };