octeon: native driver for Marvell Octeon SoC
[vpp.git] / src / plugins / dev_octeon / tx_node.c
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright (c) 2023 Cisco Systems, Inc.
3  */
4
5 #include <vlib/vlib.h>
6 #include <vppinfra/ring.h>
7 #include <vppinfra/vector/ip_csum.h>
8
9 #include <vnet/dev/dev.h>
10 #include <vnet/ethernet/ethernet.h>
11 #include <vnet/ip/ip4_packet.h>
12 #include <vnet/ip/ip6_packet.h>
13 #include <vnet/udp/udp_packet.h>
14 #include <vnet/tcp/tcp_packet.h>
15
16 #include <dev_octeon/octeon.h>
17
18 typedef struct
19 {
20   union nix_send_hdr_w0_u hdr_w0_teplate;
21   vlib_node_runtime_t *node;
22   u32 n_tx_bytes;
23   u32 n_drop;
24   vlib_buffer_t *drop[VLIB_FRAME_SIZE];
25   u32 batch_alloc_not_ready;
26   u32 batch_alloc_issue_fail;
27   u16 lmt_id;
28   u64 lmt_ioaddr;
29   lmt_line_t *lmt_lines;
30 } oct_tx_ctx_t;
31
32 static_always_inline u32
33 oct_batch_free (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq)
34 {
35   oct_txq_t *ctq = vnet_dev_get_tx_queue_data (txq);
36   u8 num_cl;
37   u64 ah;
38   u32 n_freed = 0, n;
39   oct_npa_batch_alloc_cl128_t *cl;
40
41   num_cl = ctq->ba_num_cl;
42   if (num_cl)
43     {
44       u16 off = ctq->hdr_off;
45       u32 *bi = (u32 *) ctq->ba_buffer;
46
47       for (cl = ctq->ba_buffer + ctq->ba_first_cl; num_cl > 0; num_cl--, cl++)
48         {
49           u8 count;
50           if (cl->status.ccode == ALLOC_CCODE_INVAL)
51             {
52               ctx->batch_alloc_not_ready++;
53               n_freed = bi - (u32 *) ctq->ba_buffer;
54               if (n_freed > 0)
55                 {
56                   vlib_buffer_free_no_next (vm, (u32 *) ctq->ba_buffer,
57                                             n_freed);
58                   ctq->ba_num_cl = num_cl;
59                   ctq->ba_first_cl = cl - ctq->ba_buffer;
60                   return n_freed;
61                 }
62
63               return 0;
64             }
65
66           count = cl->status.count;
67 #if (CLIB_DEBUG > 0)
68           cl->status.count = cl->status.ccode = 0;
69 #endif
70           if (PREDICT_TRUE (count == 16))
71             {
72               /* optimize for likely case where cacheline is full */
73               vlib_get_buffer_indices_with_offset (vm, (void **) cl, bi, 16,
74                                                    off);
75               bi += 16;
76             }
77           else
78             {
79               vlib_get_buffer_indices_with_offset (vm, (void **) cl, bi, count,
80                                                    off);
81               bi += count;
82             }
83         }
84
85       n_freed = bi - (u32 *) ctq->ba_buffer;
86       if (n_freed > 0)
87         vlib_buffer_free_no_next (vm, (u32 *) ctq->ba_buffer, n_freed);
88
89       /* clear status bits in each cacheline */
90       n = cl - ctq->ba_buffer;
91       for (u32 i = 0; i < n; i++)
92         ctq->ba_buffer[i].iova[0] = 0;
93
94       ctq->ba_num_cl = ctq->ba_first_cl = 0;
95     }
96
97   ah = ctq->aura_handle;
98
99   if ((n = roc_npa_aura_op_available (ah)) >= 32)
100     {
101       u64 addr, res;
102
103       n = clib_min (n, ROC_CN10K_NPA_BATCH_ALLOC_MAX_PTRS);
104
105       oct_npa_batch_alloc_compare_t cmp = {
106         .compare_s = { .aura = roc_npa_aura_handle_to_aura (ah),
107                        .stype = ALLOC_STYPE_STF,
108                        .count = n }
109       };
110
111       addr = roc_npa_aura_handle_to_base (ah) + NPA_LF_AURA_BATCH_ALLOC;
112       res = roc_atomic64_casl (cmp.as_u64, (uint64_t) ctq->ba_buffer,
113                                (i64 *) addr);
114       if (res == ALLOC_RESULT_ACCEPTED || res == ALLOC_RESULT_NOCORE)
115         {
116           ctq->ba_num_cl = (n + 15) / 16;
117           ctq->ba_first_cl = 0;
118         }
119       else
120         ctx->batch_alloc_issue_fail++;
121     }
122
123   return n_freed;
124 }
125
126 static_always_inline u8
127 oct_tx_enq1 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vlib_buffer_t *b,
128              lmt_line_t *line, u32 flags, int simple, int trace)
129 {
130   u8 n_dwords = 2;
131   u32 total_len = 0;
132   oct_tx_desc_t d = {
133     .hdr_w0 = ctx->hdr_w0_teplate,
134     .sg[0] = {
135       .segs = 1,
136       .subdc = NIX_SUBDC_SG,
137     },
138     .sg[4] = {
139       .subdc = NIX_SUBDC_SG,
140     },
141   };
142
143   if (!simple && flags & VLIB_BUFFER_NEXT_PRESENT)
144     {
145       u8 n_tail_segs = 0;
146       vlib_buffer_t *tail_segs[5], *t = b;
147
148       while (t->flags & VLIB_BUFFER_NEXT_PRESENT)
149         {
150           t = vlib_get_buffer (vm, t->next_buffer);
151           tail_segs[n_tail_segs++] = t;
152           if (n_tail_segs > 5)
153             {
154               ctx->drop[ctx->n_drop++] = t;
155               return 0;
156             }
157         }
158
159       switch (n_tail_segs)
160         {
161         case 5:
162           d.sg[7].u = (u64) vlib_buffer_get_current (tail_segs[4]);
163           total_len += d.sg[4].seg3_size = tail_segs[4]->current_length;
164           d.sg[4].segs++;
165         case 4:
166           d.sg[6].u = (u64) vlib_buffer_get_current (tail_segs[3]);
167           total_len += d.sg[4].seg2_size = tail_segs[3]->current_length;
168           d.sg[4].segs++;
169           n_dwords++;
170         case 3:
171           d.sg[5].u = (u64) vlib_buffer_get_current (tail_segs[2]);
172           total_len += d.sg[4].seg1_size = tail_segs[2]->current_length;
173           d.sg[4].segs++;
174           n_dwords++;
175         case 2:
176           d.sg[3].u = (u64) vlib_buffer_get_current (tail_segs[1]);
177           total_len += d.sg[0].seg3_size = tail_segs[1]->current_length;
178           d.sg[0].segs++;
179         case 1:
180           d.sg[2].u = (u64) vlib_buffer_get_current (tail_segs[0]);
181           total_len += d.sg[0].seg2_size = tail_segs[0]->current_length;
182           d.sg[0].segs++;
183           n_dwords++;
184         default:
185           break;
186         };
187       d.hdr_w0.sizem1 = n_dwords - 1;
188     }
189
190   if (!simple && flags & VNET_BUFFER_F_OFFLOAD)
191     {
192       vnet_buffer_oflags_t oflags = vnet_buffer (b)->oflags;
193       if (oflags & VNET_BUFFER_OFFLOAD_F_IP_CKSUM)
194         {
195           d.hdr_w1.ol3type = NIX_SENDL3TYPE_IP4_CKSUM;
196           d.hdr_w1.ol3ptr = vnet_buffer (b)->l3_hdr_offset;
197           d.hdr_w1.ol4ptr =
198             vnet_buffer (b)->l3_hdr_offset + sizeof (ip4_header_t);
199         }
200       if (oflags & VNET_BUFFER_OFFLOAD_F_UDP_CKSUM)
201         {
202           d.hdr_w1.ol4type = NIX_SENDL4TYPE_UDP_CKSUM;
203           d.hdr_w1.ol4ptr = vnet_buffer (b)->l4_hdr_offset;
204         }
205       else if (oflags & VNET_BUFFER_OFFLOAD_F_TCP_CKSUM)
206         {
207           d.hdr_w1.ol4type = NIX_SENDL4TYPE_TCP_CKSUM;
208           d.hdr_w1.ol4ptr = vnet_buffer (b)->l4_hdr_offset;
209         }
210     }
211
212   total_len += d.sg[0].seg1_size = b->current_length;
213   d.hdr_w0.total = total_len;
214   d.sg[1].u = (u64) vlib_buffer_get_current (b);
215
216   if (trace && flags & VLIB_BUFFER_IS_TRACED)
217     {
218       oct_tx_trace_t *t = vlib_add_trace (vm, ctx->node, b, sizeof (*t));
219       t->desc = d;
220       t->sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX];
221     }
222
223   for (u32 i = 0; i < n_dwords; i++)
224     line->dwords[i] = d.as_u128[i];
225
226   return n_dwords;
227 }
228
229 static_always_inline u32
230 oct_tx_enq16 (vlib_main_t *vm, oct_tx_ctx_t *ctx, vnet_dev_tx_queue_t *txq,
231               vlib_buffer_t **b, u32 n_pkts, int trace)
232 {
233   u8 dwords_per_line[16], *dpl = dwords_per_line;
234   u64 lmt_arg, ioaddr, n_lines;
235   u32 n_left, or_flags_16 = 0;
236   const u32 not_simple_flags =
237     VLIB_BUFFER_NEXT_PRESENT | VNET_BUFFER_F_OFFLOAD;
238   lmt_line_t *l = ctx->lmt_lines;
239
240   /* Data Store Memory Barrier - outer shareable domain */
241   asm volatile("dmb oshst" ::: "memory");
242
243   for (n_left = n_pkts; n_left >= 8; n_left -= 8, b += 8, l += 8)
244     {
245       u32 f0, f1, f2, f3, f4, f5, f6, f7, or_f = 0;
246       vlib_prefetch_buffer_header (b[8], LOAD);
247       or_f |= f0 = b[0]->flags;
248       or_f |= f1 = b[1]->flags;
249       vlib_prefetch_buffer_header (b[9], LOAD);
250       or_f |= f2 = b[2]->flags;
251       or_f |= f3 = b[3]->flags;
252       vlib_prefetch_buffer_header (b[10], LOAD);
253       or_f |= f4 = b[4]->flags;
254       or_f |= f5 = b[5]->flags;
255       vlib_prefetch_buffer_header (b[11], LOAD);
256       or_f |= f6 = b[6]->flags;
257       or_f |= f7 = b[7]->flags;
258       vlib_prefetch_buffer_header (b[12], LOAD);
259       or_flags_16 |= or_f;
260
261       if ((or_f & not_simple_flags) == 0)
262         {
263           int simple = 1;
264           oct_tx_enq1 (vm, ctx, b[0], l, f0, simple, trace);
265           oct_tx_enq1 (vm, ctx, b[1], l + 1, f1, simple, trace);
266           vlib_prefetch_buffer_header (b[13], LOAD);
267           oct_tx_enq1 (vm, ctx, b[2], l + 2, f2, simple, trace);
268           oct_tx_enq1 (vm, ctx, b[3], l + 3, f3, simple, trace);
269           vlib_prefetch_buffer_header (b[14], LOAD);
270           oct_tx_enq1 (vm, ctx, b[4], l + 4, f4, simple, trace);
271           oct_tx_enq1 (vm, ctx, b[5], l + 5, f5, simple, trace);
272           vlib_prefetch_buffer_header (b[15], LOAD);
273           oct_tx_enq1 (vm, ctx, b[6], l + 6, f6, simple, trace);
274           oct_tx_enq1 (vm, ctx, b[7], l + 7, f7, simple, trace);
275           dpl[0] = dpl[1] = dpl[2] = dpl[3] = 2;
276           dpl[4] = dpl[5] = dpl[6] = dpl[7] = 2;
277         }
278       else
279         {
280           int simple = 0;
281           dpl[0] = oct_tx_enq1 (vm, ctx, b[0], l, f0, simple, trace);
282           dpl[1] = oct_tx_enq1 (vm, ctx, b[1], l + 1, f1, simple, trace);
283           vlib_prefetch_buffer_header (b[13], LOAD);
284           dpl[2] = oct_tx_enq1 (vm, ctx, b[2], l + 2, f2, simple, trace);
285           dpl[3] = oct_tx_enq1 (vm, ctx, b[3], l + 3, f3, simple, trace);
286           vlib_prefetch_buffer_header (b[14], LOAD);
287           dpl[4] = oct_tx_enq1 (vm, ctx, b[4], l + 4, f4, simple, trace);
288           dpl[5] = oct_tx_enq1 (vm, ctx, b[5], l + 5, f5, simple, trace);
289           vlib_prefetch_buffer_header (b[15], LOAD);
290           dpl[6] = oct_tx_enq1 (vm, ctx, b[6], l + 6, f6, simple, trace);
291           dpl[7] = oct_tx_enq1 (vm, ctx, b[7], l + 7, f7, simple, trace);
292         }
293       dpl += 8;
294     }
295
296   for (; n_left > 0; n_left -= 1, b += 1, l += 1)
297     {
298       u32 f0 = b[0]->flags;
299       dpl++[0] = oct_tx_enq1 (vm, ctx, b[0], l, f0, 0, trace);
300       or_flags_16 |= f0;
301     }
302
303   lmt_arg = ctx->lmt_id;
304   ioaddr = ctx->lmt_ioaddr;
305   n_lines = n_pkts;
306
307   if (PREDICT_FALSE (or_flags_16 & VLIB_BUFFER_NEXT_PRESENT))
308     {
309       dpl = dwords_per_line;
310       ioaddr |= (dpl[0] - 1) << 4;
311
312       if (n_lines > 1)
313         {
314           lmt_arg |= (--n_lines) << 12;
315
316           for (u8 bit_off = 19; n_lines; n_lines--, bit_off += 3, dpl++)
317             lmt_arg |= ((u64) dpl[1] - 1) << bit_off;
318         }
319     }
320   else
321     {
322       const u64 n_dwords = 2;
323       ioaddr |= (n_dwords - 1) << 4;
324
325       if (n_lines > 1)
326         {
327           lmt_arg |= (--n_lines) << 12;
328
329           for (u8 bit_off = 19; n_lines; n_lines--, bit_off += 3)
330             lmt_arg |= (n_dwords - 1) << bit_off;
331         }
332     }
333
334   roc_lmt_submit_steorl (lmt_arg, ioaddr);
335
336   return n_pkts;
337 }
338
339 VNET_DEV_NODE_FN (oct_tx_node)
340 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
341 {
342   vnet_dev_tx_node_runtime_t *rt = vnet_dev_get_tx_node_runtime (node);
343   vnet_dev_tx_queue_t *txq = rt->tx_queue;
344   oct_txq_t *ctq = vnet_dev_get_tx_queue_data (txq);
345   u32 node_index = node->node_index;
346   u32 *from = vlib_frame_vector_args (frame);
347   u32 n, n_enq, n_left, n_pkts = frame->n_vectors;
348   vlib_buffer_t *buffers[VLIB_FRAME_SIZE + 8], **b = buffers;
349   u64 lmt_id = vm->thread_index << ROC_LMT_LINES_PER_CORE_LOG2;
350
351   oct_tx_ctx_t ctx = {
352     .node = node,
353     .hdr_w0_teplate = {
354       .aura = roc_npa_aura_handle_to_aura (ctq->aura_handle),
355       .sq = ctq->sq.qid,
356       .sizem1 = 1,
357     },
358     .lmt_id = lmt_id,
359     .lmt_ioaddr = ctq->io_addr,
360     .lmt_lines = ctq->lmt_addr + (lmt_id << ROC_LMT_LINE_SIZE_LOG2),
361   };
362
363   vlib_get_buffers (vm, vlib_frame_vector_args (frame), b, n_pkts);
364   for (int i = 0; i < 8; i++)
365     b[n_pkts + i] = b[n_pkts - 1];
366
367   vnet_dev_tx_queue_lock_if_needed (txq);
368
369   n_enq = ctq->n_enq;
370   n_enq -= oct_batch_free (vm, &ctx, txq);
371
372   if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
373     {
374       for (n_left = clib_min (n_pkts, txq->size - n_enq), n = 0; n_left >= 16;
375            n_left -= 16, b += 16)
376         n += oct_tx_enq16 (vm, &ctx, txq, b, 16, /* trace */ 1);
377
378       if (n_left)
379         n += oct_tx_enq16 (vm, &ctx, txq, b, n_left, /* trace */ 1);
380     }
381   else
382     {
383       for (n_left = clib_min (n_pkts, txq->size - n_enq), n = 0; n_left >= 16;
384            n_left -= 16, b += 16)
385         n += oct_tx_enq16 (vm, &ctx, txq, b, 16, /* trace */ 0);
386
387       if (n_left)
388         n += oct_tx_enq16 (vm, &ctx, txq, b, n_left, /* trace */ 0);
389     }
390
391   ctq->n_enq = n_enq + n;
392
393   if (n < n_pkts)
394     {
395       n = n_pkts - n;
396       vlib_buffer_free (vm, from + n, n);
397       vlib_error_count (vm, node->node_index, OCT_TX_NODE_CTR_NO_FREE_SLOTS,
398                         n);
399       n_pkts -= ctx.n_drop;
400     }
401
402   if (ctx.n_drop)
403     vlib_error_count (vm, node->node_index, OCT_TX_NODE_CTR_CHAIN_TOO_LONG,
404                       ctx.n_drop);
405
406   if (ctx.batch_alloc_not_ready)
407     vlib_error_count (vm, node_index,
408                       OCT_TX_NODE_CTR_AURA_BATCH_ALLOC_NOT_READY,
409                       ctx.batch_alloc_not_ready);
410
411   if (ctx.batch_alloc_issue_fail)
412     vlib_error_count (vm, node_index,
413                       OCT_TX_NODE_CTR_AURA_BATCH_ALLOC_ISSUE_FAIL,
414                       ctx.batch_alloc_issue_fail);
415
416   vnet_dev_tx_queue_unlock_if_needed (txq);
417
418   if (ctx.n_drop)
419     {
420       u32 bi[VLIB_FRAME_SIZE];
421       vlib_get_buffer_indices (vm, ctx.drop, bi, ctx.n_drop);
422       vlib_buffer_free (vm, bi, ctx.n_drop);
423       n_pkts -= ctx.n_drop;
424     }
425
426   return n_pkts;
427 }