ena: Amazon Elastic Network Adapter (ENA) native driver
[vpp.git] / src / plugins / dev_ena / aq.c
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright(c) 2023 Cisco Systems, Inc.
3  */
4
5 #include <vlib/vlib.h>
6 #include <vnet/dev/dev.h>
7
8 #include <dev_ena/ena.h>
9 #include <dev_ena/ena_inlines.h>
10 #include <vnet/ethernet/ethernet.h>
11
12 VLIB_REGISTER_LOG_CLASS (ena_log, static) = {
13   .class_name = "ena",
14   .subclass_name = "admin",
15 };
16
17 VLIB_REGISTER_LOG_CLASS (ena_stats_log, static) = {
18   .class_name = "ena",
19   .subclass_name = "admin-stats",
20 };
21
22 ena_aq_feat_info_t feat_info[] = {
23 #define _(v, ver, gt, st, n, s)                                               \
24   [v] = { .name = #n,                                                         \
25           .version = (ver),                                                   \
26           .data_sz = sizeof (s),                                              \
27           .get = (gt),                                                        \
28           .set = (st) },
29   foreach_ena_aq_feature_id
30 #undef _
31 };
32
33 ena_aq_feat_info_t *
34 ena_aq_get_feat_info (ena_aq_feature_id_t id)
35 {
36   if (id >= ARRAY_LEN (feat_info) || feat_info[id].data_sz == 0)
37     return 0;
38
39   return feat_info + id;
40 }
41
42 void
43 ena_aq_free (vlib_main_t *vm, vnet_dev_t *dev)
44 {
45   ena_device_t *ed = vnet_dev_get_data (dev);
46   vnet_dev_dma_mem_free (vm, dev, ed->aq.cq_entries);
47   vnet_dev_dma_mem_free (vm, dev, ed->aq.sq_entries);
48   ed->aq.depth = 0;
49 }
50
51 vnet_dev_rv_t
52 ena_aq_olloc (vlib_main_t *vm, vnet_dev_t *dev, u16 depth)
53 {
54   ena_device_t *ed = vnet_dev_get_data (dev);
55   vnet_dev_dma_mem_free (vm, dev, ed->aq.cq_entries);
56   vnet_dev_dma_mem_free (vm, dev, ed->aq.sq_entries);
57   u32 sq_alloc_sz = sizeof (ena_aq_sq_entry_t) * depth;
58   u32 cq_alloc_sz = sizeof (ena_aq_cq_entry_t) * depth;
59   vnet_dev_rv_t rv;
60
61   ASSERT (ed->aq.sq_entries == 0);
62   ASSERT (ed->aq.cq_entries == 0);
63
64   rv = vnet_dev_dma_mem_alloc (vm, dev, sq_alloc_sz, 0,
65                                (void **) &ed->aq.sq_entries);
66   if (rv != VNET_DEV_OK)
67     goto err;
68
69   rv = vnet_dev_dma_mem_alloc (vm, dev, cq_alloc_sz, 0,
70                                (void **) &ed->aq.cq_entries);
71   if (rv != VNET_DEV_OK)
72     goto err;
73
74   ed->aq.depth = depth;
75
76   return VNET_DEV_OK;
77 err:
78   ena_aq_free (vm, dev);
79   return rv;
80 }
81
82 vnet_dev_rv_t
83 ena_aq_start (vlib_main_t *vm, vnet_dev_t *dev)
84 {
85   ena_device_t *ed = vnet_dev_get_data (dev);
86   u16 depth = ed->aq.depth;
87   u32 sq_alloc_sz = sizeof (ena_aq_sq_entry_t) * depth;
88   u32 cq_alloc_sz = sizeof (ena_aq_cq_entry_t) * depth;
89
90   ASSERT (ed->aq_started == 0);
91
92   ena_reg_aq_caps_t aq_caps = {
93     .depth = depth,
94     .entry_size = sizeof (ena_aq_sq_entry_t),
95   };
96
97   ena_reg_acq_caps_t acq_caps = {
98     .depth = depth,
99     .entry_size = sizeof (ena_aq_cq_entry_t),
100   };
101
102   clib_memset (ed->aq.sq_entries, 0, sq_alloc_sz);
103   clib_memset (ed->aq.cq_entries, 0, cq_alloc_sz);
104
105   ed->aq.sq_next = 0;
106   ed->aq.cq_head = 0;
107
108   ena_reg_set_dma_addr (vm, dev, ENA_REG_AQ_BASE_LO, ENA_REG_AQ_BASE_HI,
109                         ed->aq.sq_entries);
110   ena_reg_set_dma_addr (vm, dev, ENA_REG_ACQ_BASE_LO, ENA_REG_ACQ_BASE_HI,
111                         ed->aq.cq_entries);
112
113   ena_reg_write (dev, ENA_REG_AQ_CAPS, &aq_caps);
114   ena_reg_write (dev, ENA_REG_ACQ_CAPS, &acq_caps);
115
116   ed->aq_started = 1;
117
118   return VNET_DEV_OK;
119 }
120
121 void
122 ena_aq_stop (vlib_main_t *vm, vnet_dev_t *dev)
123 {
124   ena_device_t *ed = vnet_dev_get_data (dev);
125   ena_reg_aq_caps_t aq_caps = {};
126   ena_reg_acq_caps_t acq_caps = {};
127
128   if (ed->aq_started)
129     {
130       ena_reg_write (dev, ENA_REG_AQ_CAPS, &aq_caps);
131       ena_reg_write (dev, ENA_REG_ACQ_CAPS, &acq_caps);
132       ed->aq_started = 0;
133     }
134 }
135 vnet_dev_rv_t
136 ena_aq_req (vlib_main_t *vm, vnet_dev_t *dev, ena_aq_opcode_t opcode,
137             void *sqe_data, u8 sqe_data_sz, void *cqe_data, u8 cqe_data_sz)
138 {
139   ena_device_t *ed = vnet_dev_get_data (dev);
140   u32 next = ed->aq.sq_next++;
141   u32 index = next & pow2_mask (ENA_ADMIN_QUEUE_LOG2_DEPTH);
142   u8 phase = 1 & (~(next >> ENA_ADMIN_QUEUE_LOG2_DEPTH));
143   ena_aq_sq_entry_t *sqe = ed->aq.sq_entries + index;
144   ena_aq_cq_entry_t *cqe = ed->aq.cq_entries + index;
145   f64 suspend_time = 1e-6;
146
147   clib_memcpy_fast (&sqe->data, sqe_data, sqe_data_sz);
148   sqe->opcode = opcode;
149   sqe->command_id = index;
150   sqe->phase = phase;
151
152   ena_reg_write (dev, ENA_REG_AQ_DB, &ed->aq.sq_next);
153
154   while (cqe->phase != phase)
155     {
156       vlib_process_suspend (vm, suspend_time);
157       suspend_time *= 2;
158       if (suspend_time > 1e-3)
159         {
160           log_err (dev, "admin queue timeout (opcode %U)",
161                    format_ena_aq_opcode, opcode);
162           return VNET_DEV_ERR_TIMEOUT;
163         }
164     }
165
166   if (cqe->status != ENA_ADMIN_COMPL_STATUS_SUCCESS)
167     {
168       log_err (dev,
169                "cqe[%u]: opcode %U status %U ext_status %u sq_head_idx %u",
170                cqe - ed->aq.cq_entries, format_ena_aq_opcode, opcode,
171                format_ena_aq_status, cqe->status, cqe->extended_status,
172                cqe->sq_head_indx);
173       return VNET_DEV_ERR_DEVICE_NO_REPLY;
174     }
175
176   log_debug (dev, "cqe: status %u ext_status %u sq_head_idx %u", cqe->status,
177              cqe->extended_status, cqe->sq_head_indx);
178
179   if (cqe_data && cqe_data_sz)
180     clib_memcpy_fast (cqe_data, &cqe->data, cqe_data_sz);
181   return VNET_DEV_OK;
182 }
183
184 vnet_dev_rv_t
185 ena_aq_set_feature (vlib_main_t *vm, vnet_dev_t *dev,
186                     ena_aq_feature_id_t feat_id, void *data)
187 {
188   vnet_dev_rv_t rv;
189
190   struct
191   {
192     ena_aq_aq_ctrl_buff_info_t control_buffer;
193     ena_aq_get_set_feature_common_desc_t feat_common;
194     u32 data[11];
195   } fd = {
196     .feat_common.feature_id = feat_id,
197     .feat_common.feature_version = feat_info[feat_id].version,
198   };
199
200   log_debug (dev, "set_feature(%s):\n  %U", feat_info[feat_id].name,
201              format_ena_aq_feat_desc, feat_id, data);
202
203   ASSERT (feat_info[feat_id].data_sz > 1);
204   clib_memcpy (&fd.data, data, feat_info[feat_id].data_sz);
205
206   rv = ena_aq_req (vm, dev, ENA_AQ_OPCODE_SET_FEATURE, &fd, sizeof (fd), 0, 0);
207
208   if (rv != VNET_DEV_OK)
209     log_err (dev, "get_feature(%U) failed", format_ena_aq_feat_name, feat_id);
210
211   return rv;
212 }
213
214 vnet_dev_rv_t
215 ena_aq_get_feature (vlib_main_t *vm, vnet_dev_t *dev,
216                     ena_aq_feature_id_t feat_id, void *data)
217 {
218   vnet_dev_rv_t rv;
219
220   struct
221   {
222     ena_aq_aq_ctrl_buff_info_t control_buffer;
223     ena_aq_get_set_feature_common_desc_t feat_common;
224     u32 data[11];
225   } fd = {
226     .feat_common.feature_id = feat_id,
227     .feat_common.feature_version = feat_info[feat_id].version,
228   };
229
230   rv = ena_aq_req (vm, dev, ENA_AQ_OPCODE_GET_FEATURE, &fd, sizeof (fd), data,
231                    feat_info[feat_id].data_sz);
232
233   if (rv != VNET_DEV_OK)
234     {
235       log_err (dev, "get_feature(%U) failed", format_ena_aq_feat_name,
236                feat_id);
237       return rv;
238     }
239
240   ASSERT (feat_info[feat_id].data_sz > 1);
241
242   log_debug (dev, "get_feature(%s):\n  %U", feat_info[feat_id].name,
243              format_ena_aq_feat_desc, feat_id, data);
244
245   return 0;
246 }
247
248 vnet_dev_rv_t
249 ena_aq_create_sq (vlib_main_t *vm, vnet_dev_t *dev,
250                   ena_aq_create_sq_cmd_t *cmd, ena_aq_create_sq_resp_t *resp)
251 {
252   vnet_dev_rv_t rv;
253
254   log_debug (dev, "create_sq_cmd_req:\n  %U", format_ena_aq_create_sq_cmd,
255              cmd);
256
257   rv = ena_aq_req (vm, dev, ENA_AQ_OPCODE_CREATE_SQ, cmd, sizeof (*cmd), resp,
258                    sizeof (*resp));
259
260   if (rv != VNET_DEV_OK)
261     log_debug (dev, "create_sq_cmd_resp:\n  %U", format_ena_aq_create_sq_resp,
262                resp);
263   return rv;
264 }
265
266 vnet_dev_rv_t
267 ena_aq_create_cq (vlib_main_t *vm, vnet_dev_t *dev,
268                   ena_aq_create_cq_cmd_t *cmd, ena_aq_create_cq_resp_t *resp)
269 {
270   vnet_dev_rv_t rv;
271
272   log_debug (dev, "create_cq_cmd_req:\n  %U", format_ena_aq_create_cq_cmd,
273              cmd);
274
275   rv = ena_aq_req (vm, dev, ENA_AQ_OPCODE_CREATE_CQ, cmd, sizeof (*cmd), resp,
276                    sizeof (*resp));
277
278   if (rv != VNET_DEV_OK)
279     log_debug (dev, "create_cq_cmd_resp:\n  %U", format_ena_aq_create_cq_resp,
280                resp);
281
282   return rv;
283 }
284
285 vnet_dev_rv_t
286 ena_aq_destroy_sq (vlib_main_t *vm, vnet_dev_t *dev,
287                    ena_aq_destroy_sq_cmd_t *cmd)
288 {
289   log_debug (dev, "destroy_sq_cmd_req:\n  %U", format_ena_aq_destroy_sq_cmd,
290              cmd);
291
292   return ena_aq_req (vm, dev, ENA_AQ_OPCODE_DESTROY_SQ, cmd, sizeof (*cmd), 0,
293                      0);
294 }
295
296 vnet_dev_rv_t
297 ena_aq_destroy_cq (vlib_main_t *vm, vnet_dev_t *dev,
298                    ena_aq_destroy_cq_cmd_t *cmd)
299 {
300   log_debug (dev, "destroy_cq_cmd_req:\n  %U", format_ena_aq_destroy_cq_cmd,
301              cmd);
302
303   return ena_aq_req (vm, dev, ENA_AQ_OPCODE_DESTROY_CQ, cmd, sizeof (*cmd), 0,
304                      0);
305 }
306
307 vnet_dev_rv_t
308 ena_aq_get_stats (vlib_main_t *vm, vnet_dev_t *dev, ena_aq_stats_type_t type,
309                   ena_aq_stats_scope_t scope, u16 queue_idx, void *data)
310 {
311   vnet_dev_rv_t rv;
312   format_function_t *ff = 0;
313   u8 data_sz[] = {
314     [ENA_ADMIN_STATS_TYPE_BASIC] = sizeof (ena_aq_basic_stats_t),
315     [ENA_ADMIN_STATS_TYPE_EXTENDED] = 0,
316     [ENA_ADMIN_STATS_TYPE_ENI] = sizeof (ena_aq_eni_stats_t),
317   };
318
319   char *type_str[] = {
320 #define _(n, s) [n] = #s,
321     foreach_ena_aq_stats_type
322 #undef _
323   };
324
325   char *scope_str[] = {
326 #define _(n, s) [n] = #s,
327     foreach_ena_aq_stats_scope
328 #undef _
329   };
330
331   ena_aq_get_stats_cmd_t cmd = {
332     .type = type,
333     .scope = scope,
334     .queue_idx = scope == ENA_ADMIN_STATS_SCOPE_SPECIFIC_QUEUE ? queue_idx : 0,
335     .device_id = 0xffff,
336   };
337
338   if ((rv = ena_aq_req (vm, dev, ENA_AQ_OPCODE_GET_STATS, &cmd, sizeof (cmd),
339                         data, data_sz[type])))
340     {
341       ena_stats_log_err (dev, "get_stats(%s, %s) failed", type_str[type],
342                          scope_str[scope]);
343       return rv;
344     }
345
346   if (type == ENA_ADMIN_STATS_TYPE_BASIC)
347     ff = format_ena_aq_basic_stats;
348   else if (type == ENA_ADMIN_STATS_TYPE_ENI)
349     ff = format_ena_aq_eni_stats;
350
351   if (ff)
352     ena_stats_log_debug (dev, "get_stats(%s, %s, %u):\n  %U", type_str[type],
353                          scope_str[scope], queue_idx, ff, data);
354   else
355     ena_stats_log_debug (dev, "get_stats(%s, %s, %u): unknown data",
356                          type_str[type], scope_str[scope], queue_idx);
357
358   return VNET_DEV_OK;
359 }